This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | if(!defined('sugarEntry') || !sugarEntry) die('Not A Valid Entry Point'); |
||
3 | /********************************************************************************* |
||
4 | * SugarCRM Community Edition is a customer relationship management program developed by |
||
5 | * SugarCRM, Inc. Copyright (C) 2004-2013 SugarCRM Inc. |
||
6 | |||
7 | * SuiteCRM is an extension to SugarCRM Community Edition developed by Salesagility Ltd. |
||
8 | * Copyright (C) 2011 - 2014 Salesagility Ltd. |
||
9 | * |
||
10 | * This program is free software; you can redistribute it and/or modify it under |
||
11 | * the terms of the GNU Affero General Public License version 3 as published by the |
||
12 | * Free Software Foundation with the addition of the following permission added |
||
13 | * to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK |
||
14 | * IN WHICH THE COPYRIGHT IS OWNED BY SUGARCRM, SUGARCRM DISCLAIMS THE WARRANTY |
||
15 | * OF NON INFRINGEMENT OF THIRD PARTY RIGHTS. |
||
16 | * |
||
17 | * This program is distributed in the hope that it will be useful, but WITHOUT |
||
18 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
||
19 | * FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more |
||
20 | * details. |
||
21 | * |
||
22 | * You should have received a copy of the GNU Affero General Public License along with |
||
23 | * this program; if not, see http://www.gnu.org/licenses or write to the Free |
||
24 | * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA |
||
25 | * 02110-1301 USA. |
||
26 | * |
||
27 | * You can contact SugarCRM, Inc. headquarters at 10050 North Wolfe Road, |
||
28 | * SW2-130, Cupertino, CA 95014, USA. or at email address [email protected]. |
||
29 | * |
||
30 | * The interactive user interfaces in modified source and object code versions |
||
31 | * of this program must display Appropriate Legal Notices, as required under |
||
32 | * Section 5 of the GNU Affero General Public License version 3. |
||
33 | * |
||
34 | * In accordance with Section 7(b) of the GNU Affero General Public License version 3, |
||
35 | * these Appropriate Legal Notices must retain the display of the "Powered by |
||
36 | * SugarCRM" logo and "Supercharged by SuiteCRM" logo. If the display of the logos is not |
||
37 | * reasonably feasible for technical reasons, the Appropriate Legal Notices must |
||
38 | * display the words "Powered by SugarCRM" and "Supercharged by SuiteCRM". |
||
39 | ********************************************************************************/ |
||
40 | |||
41 | /********************************************************************************* |
||
42 | |||
43 | * Description: This file handles the Data base functionality for the application. |
||
44 | * It acts as the DB abstraction layer for the application. It depends on helper classes |
||
45 | * which generate the necessary SQL. This sql is then passed to PEAR DB classes. |
||
46 | * The helper class is chosen in DBManagerFactory, which is driven by 'db_type' in 'dbconfig' under config.php. |
||
47 | * |
||
48 | * All the functions in this class will work with any bean which implements the meta interface. |
||
49 | * The passed bean is passed to helper class which uses these functions to generate correct sql. |
||
50 | * |
||
51 | * The meta interface has the following functions: |
||
52 | * getTableName() Returns table name of the object. |
||
53 | * getFieldDefinitions() Returns a collection of field definitions in order. |
||
54 | * getFieldDefintion(name) Return field definition for the field. |
||
55 | * getFieldValue(name) Returns the value of the field identified by name. |
||
56 | * If the field is not set, the function will return boolean FALSE. |
||
57 | * getPrimaryFieldDefinition() Returns the field definition for primary key |
||
58 | * |
||
59 | * The field definition is an array with the following keys: |
||
60 | * |
||
61 | * name This represents name of the field. This is a required field. |
||
62 | * type This represents type of the field. This is a required field and valid values are: |
||
63 | * int |
||
64 | * long |
||
65 | * varchar |
||
66 | * text |
||
67 | * date |
||
68 | * datetime |
||
69 | * double |
||
70 | * float |
||
71 | * uint |
||
72 | * ulong |
||
73 | * time |
||
74 | * short |
||
75 | * enum |
||
76 | * length This is used only when the type is varchar and denotes the length of the string. |
||
77 | * The max value is 255. |
||
78 | * enumvals This is a list of valid values for an enum separated by "|". |
||
79 | * It is used only if the type is ?enum?; |
||
80 | * required This field dictates whether it is a required value. |
||
81 | * The default value is ?FALSE?. |
||
82 | * isPrimary This field identifies the primary key of the table. |
||
83 | * If none of the fields have this flag set to ?TRUE?, |
||
84 | * the first field definition is assume to be the primary key. |
||
85 | * Default value for this field is ?FALSE?. |
||
86 | * default This field sets the default value for the field definition. |
||
87 | * |
||
88 | * |
||
89 | * Portions created by SugarCRM are Copyright (C) SugarCRM, Inc. |
||
90 | * All Rights Reserved. |
||
91 | * Contributor(s): ______________________________________.. |
||
92 | ********************************************************************************/ |
||
93 | |||
94 | /** |
||
95 | * SQL Server (mssql) manager |
||
96 | */ |
||
97 | class MssqlManager extends DBManager |
||
98 | { |
||
99 | /** |
||
100 | * @see DBManager::$dbType |
||
101 | */ |
||
102 | public $dbType = 'mssql'; |
||
103 | public $dbName = 'MsSQL'; |
||
104 | public $variant = 'mssql'; |
||
105 | public $label = 'LBL_MSSQL'; |
||
106 | |||
107 | protected $capabilities = array( |
||
108 | "affected_rows" => true, |
||
109 | "select_rows" => true, |
||
110 | 'fulltext' => true, |
||
111 | 'limit_subquery' => true, |
||
112 | "fix:expandDatabase" => true, // Support expandDatabase fix |
||
113 | "create_user" => true, |
||
114 | "create_db" => true, |
||
115 | ); |
||
116 | |||
117 | /** |
||
118 | * Maximum length of identifiers |
||
119 | */ |
||
120 | protected $maxNameLengths = array( |
||
121 | 'table' => 128, |
||
122 | 'column' => 128, |
||
123 | 'index' => 128, |
||
124 | 'alias' => 128 |
||
125 | ); |
||
126 | |||
127 | protected $type_map = array( |
||
128 | 'int' => 'int', |
||
129 | 'double' => 'float', |
||
130 | 'float' => 'float', |
||
131 | 'uint' => 'int', |
||
132 | 'ulong' => 'int', |
||
133 | 'long' => 'bigint', |
||
134 | 'short' => 'smallint', |
||
135 | 'varchar' => 'varchar', |
||
136 | 'text' => 'text', |
||
137 | 'longtext' => 'text', |
||
138 | 'date' => 'datetime', |
||
139 | 'enum' => 'varchar', |
||
140 | 'relate' => 'varchar', |
||
141 | 'multienum'=> 'text', |
||
142 | 'html' => 'text', |
||
143 | 'longhtml' => 'text', |
||
144 | 'datetime' => 'datetime', |
||
145 | 'datetimecombo' => 'datetime', |
||
146 | 'time' => 'datetime', |
||
147 | 'bool' => 'bit', |
||
148 | 'tinyint' => 'tinyint', |
||
149 | 'char' => 'char', |
||
150 | 'blob' => 'image', |
||
151 | 'longblob' => 'image', |
||
152 | 'currency' => 'decimal(26,6)', |
||
153 | 'decimal' => 'decimal', |
||
154 | 'decimal2' => 'decimal', |
||
155 | 'id' => 'varchar(36)', |
||
156 | 'url' => 'varchar', |
||
157 | 'encrypt' => 'varchar', |
||
158 | 'file' => 'varchar', |
||
159 | 'decimal_tpl' => 'decimal(%d, %d)', |
||
160 | ); |
||
161 | |||
162 | protected $connectOptions = null; |
||
163 | |||
164 | /** |
||
165 | * @see DBManager::connect() |
||
166 | */ |
||
167 | public function connect(array $configOptions = null, $dieOnError = false) |
||
168 | { |
||
169 | global $sugar_config; |
||
170 | |||
171 | if (is_null($configOptions)) |
||
172 | $configOptions = $sugar_config['dbconfig']; |
||
173 | |||
174 | //SET DATEFORMAT to 'YYYY-MM-DD'' |
||
175 | ini_set('mssql.datetimeconvert', '0'); |
||
176 | |||
177 | //set the text size and textlimit to max number so that blob columns are not truncated |
||
178 | ini_set('mssql.textlimit','2147483647'); |
||
179 | ini_set('mssql.textsize','2147483647'); |
||
180 | ini_set('mssql.charset','UTF-8'); |
||
181 | |||
182 | if(!empty($configOptions['db_host_instance'])) { |
||
183 | $configOptions['db_host_instance'] = trim($configOptions['db_host_instance']); |
||
184 | } |
||
185 | //set the connections parameters |
||
186 | if (empty($configOptions['db_host_instance'])) { |
||
187 | $connect_param = $configOptions['db_host_name']; |
||
188 | } else { |
||
189 | $connect_param = $configOptions['db_host_name']."\\".$configOptions['db_host_instance']; |
||
190 | } |
||
191 | |||
192 | //create persistent connection |
||
193 | if ($this->getOption('persistent')) { |
||
194 | $this->database =@mssql_pconnect( |
||
195 | $connect_param , |
||
196 | $configOptions['db_user_name'], |
||
197 | $configOptions['db_password'] |
||
198 | ); |
||
199 | } |
||
200 | //if no persistent connection created, then create regular connection |
||
201 | if(!$this->database){ |
||
202 | $this->database = mssql_connect( |
||
203 | $connect_param , |
||
204 | $configOptions['db_user_name'], |
||
205 | $configOptions['db_password'] |
||
206 | ); |
||
207 | if(!$this->database){ |
||
208 | $GLOBALS['log']->fatal("Could not connect to server ".$configOptions['db_host_name']. |
||
209 | " as ".$configOptions['db_user_name']."."); |
||
210 | if($dieOnError) { |
||
211 | sugar_die($GLOBALS['app_strings']['ERR_NO_DB']); |
||
212 | } else { |
||
213 | return false; |
||
214 | } |
||
215 | } |
||
216 | if($this->database && $this->getOption('persistent')){ |
||
217 | $_SESSION['administrator_error'] = "<B>Severe Performance Degradation: Persistent Database Connections " |
||
218 | . "not working. Please set \$sugar_config['dbconfigoption']['persistent'] to false in your " |
||
219 | . "config.php file</B>"; |
||
220 | } |
||
221 | } |
||
222 | //make sure connection exists |
||
223 | if(!$this->database) { |
||
224 | if($dieOnError) { |
||
225 | sugar_die($GLOBALS['app_strings']['ERR_NO_DB']); |
||
226 | } else { |
||
227 | return false; |
||
228 | } |
||
229 | } |
||
230 | |||
231 | //select database |
||
232 | |||
233 | //Adding sleep and retry for mssql connection. We have come across scenarios when |
||
234 | //an error is thrown.' Unable to select database'. Following will try to connect to |
||
235 | //mssql db maximum number of 5 times at the interval of .2 second. If can not connect |
||
236 | //it will throw an Unable to select database message. |
||
237 | |||
238 | if(!empty($configOptions['db_name']) && !@mssql_select_db($configOptions['db_name'], $this->database)){ |
||
239 | $connected = false; |
||
240 | for($i=0;$i<5;$i++){ |
||
241 | usleep(200000); |
||
242 | if(@mssql_select_db($configOptions['db_name'], $this->database)){ |
||
243 | $connected = true; |
||
244 | break; |
||
245 | } |
||
246 | } |
||
247 | if(!$connected){ |
||
248 | $GLOBALS['log']->fatal( "Unable to select database {$configOptions['db_name']}"); |
||
249 | if($dieOnError) { |
||
250 | if(isset($GLOBALS['app_strings']['ERR_NO_DB'])) { |
||
251 | sugar_die($GLOBALS['app_strings']['ERR_NO_DB']); |
||
252 | } else { |
||
253 | sugar_die("Could not connect to the database. Please refer to suitecrm.log for details."); |
||
254 | } |
||
255 | } else { |
||
256 | return false; |
||
257 | } |
||
258 | } |
||
259 | } |
||
260 | |||
261 | if(!$this->checkError('Could Not Connect', $dieOnError)) |
||
262 | $GLOBALS['log']->info("connected to db"); |
||
263 | |||
264 | $this->connectOptions = $configOptions; |
||
265 | |||
266 | $GLOBALS['log']->info("Connect:".$this->database); |
||
267 | return true; |
||
268 | } |
||
269 | |||
270 | /** |
||
271 | * @see DBManager::version() |
||
272 | */ |
||
273 | public function version() |
||
274 | { |
||
275 | return $this->getOne("SELECT @@VERSION as version"); |
||
276 | } |
||
277 | |||
278 | /** |
||
279 | * @see DBManager::query() |
||
280 | */ |
||
281 | public function query($sql, $dieOnError = false, $msg = '', $suppress = false, $keepResult = false) |
||
282 | { |
||
283 | if(is_array($sql)) { |
||
284 | return $this->queryArray($sql, $dieOnError, $msg, $suppress); |
||
285 | } |
||
286 | // Flag if there are odd number of single quotes |
||
287 | if ((substr_count($sql, "'") & 1)) |
||
288 | $GLOBALS['log']->error("SQL statement[" . $sql . "] has odd number of single quotes."); |
||
289 | |||
290 | $sql = $this->_appendN($sql); |
||
291 | |||
292 | $GLOBALS['log']->info('Query:' . $sql); |
||
293 | $this->checkConnection(); |
||
294 | $this->countQuery($sql); |
||
0 ignored issues
–
show
|
|||
295 | $this->query_time = microtime(true); |
||
0 ignored issues
–
show
The property
$query_time was declared of type integer , but microtime(true) is of type double . Maybe add a type cast?
This check looks for assignments to scalar types that may be of the wrong type. To ensure the code behaves as expected, it may be a good idea to add an explicit type cast. $answer = 42;
$correct = false;
$correct = (bool) $answer;
![]() |
|||
296 | |||
297 | // Bug 34892 - Clear out previous error message by checking the @@ERROR global variable |
||
298 | @mssql_query("SELECT @@ERROR", $this->database); |
||
0 ignored issues
–
show
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.
If you suppress an error, we recommend checking for the error condition explicitly: // For example instead of
@mkdir($dir);
// Better use
if (@mkdir($dir) === false) {
throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
![]() |
|||
299 | |||
300 | $result = $suppress?@mssql_query($sql, $this->database):mssql_query($sql, $this->database); |
||
301 | |||
302 | if (!$result) { |
||
303 | // awu Bug 10657: ignoring mssql error message 'Changed database context to' - an intermittent |
||
304 | // and difficult to reproduce error. The message is only a warning, and does |
||
305 | // not affect the functionality of the query |
||
306 | $sqlmsg = mssql_get_last_message(); |
||
307 | $sqlpos = strpos($sqlmsg, 'Changed database context to'); |
||
308 | $sqlpos2 = strpos($sqlmsg, 'Warning:'); |
||
309 | $sqlpos3 = strpos($sqlmsg, 'Checking identity information:'); |
||
310 | |||
311 | if ($sqlpos !== false || $sqlpos2 !== false || $sqlpos3 !== false) // if sqlmsg has 'Changed database context to', just log it |
||
312 | $GLOBALS['log']->debug($sqlmsg . ": " . $sql ); |
||
313 | else { |
||
314 | $GLOBALS['log']->fatal($sqlmsg . ": " . $sql ); |
||
315 | if($dieOnError) |
||
316 | sugar_die('SQL Error : ' . $sqlmsg); |
||
317 | else |
||
318 | echo 'SQL Error : ' . $sqlmsg; |
||
319 | } |
||
320 | } |
||
321 | |||
322 | $this->query_time = microtime(true) - $this->query_time; |
||
0 ignored issues
–
show
The property
$query_time was declared of type integer , but microtime(true) - $this->query_time is of type double . Maybe add a type cast?
This check looks for assignments to scalar types that may be of the wrong type. To ensure the code behaves as expected, it may be a good idea to add an explicit type cast. $answer = 42;
$correct = false;
$correct = (bool) $answer;
![]() |
|||
323 | $GLOBALS['log']->info('Query Execution Time:'.$this->query_time); |
||
324 | |||
325 | |||
326 | $this->checkError($msg.' Query Failed: ' . $sql, $dieOnError); |
||
327 | |||
328 | return $result; |
||
329 | } |
||
330 | |||
331 | /** |
||
332 | * This function take in the sql for a union query, the start and offset, |
||
333 | * and wraps it around an "mssql friendly" limit query |
||
334 | * |
||
335 | * @param string $sql |
||
336 | * @param int $start record to start at |
||
337 | * @param int $count number of records to retrieve |
||
338 | * @return string SQL statement |
||
339 | */ |
||
340 | private function handleUnionLimitQuery($sql, $start, $count) |
||
341 | { |
||
342 | //set the start to 0, no negs |
||
343 | if ($start < 0) |
||
344 | $start=0; |
||
345 | |||
346 | $GLOBALS['log']->debug(print_r(func_get_args(),true)); |
||
347 | |||
348 | $this->lastsql = $sql; |
||
349 | |||
350 | //change the casing to lower for easier string comparison, and trim whitespaces |
||
351 | $sql = strtolower(trim($sql)) ; |
||
352 | |||
353 | //set default sql |
||
354 | $limitUnionSQL = $sql; |
||
355 | $order_by_str = 'order by'; |
||
356 | |||
357 | //make array of order by's. substring approach was proving too inconsistent |
||
358 | $orderByArray = explode($order_by_str, $sql); |
||
359 | $unionOrderBy = ''; |
||
360 | $rowNumOrderBy = ''; |
||
361 | |||
362 | //count the number of array elements |
||
363 | $unionOrderByCount = count($orderByArray); |
||
364 | $arr_count = 0; |
||
365 | |||
366 | //process if there are elements |
||
367 | if ($unionOrderByCount){ |
||
368 | //we really want the last order by, so reconstruct string |
||
369 | //adding a 1 to count, as we dont wish to process the last element |
||
370 | $unionsql = ''; |
||
371 | while ($unionOrderByCount>$arr_count+1) { |
||
372 | $unionsql .= $orderByArray[$arr_count]; |
||
373 | $arr_count = $arr_count+1; |
||
374 | //add an "order by" string back if we are coming into loop again |
||
375 | //remember they were taken out when array was created |
||
376 | if ($unionOrderByCount>$arr_count+1) { |
||
377 | $unionsql .= "order by"; |
||
378 | } |
||
379 | } |
||
380 | //grab the last order by element, set both order by's' |
||
381 | $unionOrderBy = $orderByArray[$arr_count]; |
||
382 | $rowNumOrderBy = $unionOrderBy; |
||
383 | |||
384 | //if last element contains a "select", then this is part of the union query, |
||
385 | //and there is no order by to use |
||
386 | if (strpos($unionOrderBy, "select")) { |
||
387 | $unionsql = $sql; |
||
388 | //with no guidance on what to use for required order by in rownumber function, |
||
389 | //resort to using name column. |
||
390 | $rowNumOrderBy = 'id'; |
||
391 | $unionOrderBy = ""; |
||
392 | } |
||
393 | } |
||
394 | else { |
||
395 | //there are no order by elements, so just pass back string |
||
396 | $unionsql = $sql; |
||
397 | //with no guidance on what to use for required order by in rownumber function, |
||
398 | //resort to using name column. |
||
399 | $rowNumOrderBy = 'id'; |
||
400 | $unionOrderBy = ''; |
||
401 | } |
||
402 | //Unions need the column name being sorted on to match across all queries in Union statement |
||
403 | //so we do not want to strip the alias like in other queries. Just add the "order by" string and |
||
404 | //pass column name as is |
||
405 | if ($unionOrderBy != '') { |
||
406 | $unionOrderBy = ' order by ' . $unionOrderBy; |
||
407 | } |
||
408 | |||
409 | //Bug 56560, use top query in conjunction with rownumber() function |
||
410 | //to create limit query when paging is needed. Otherwise, |
||
411 | //it shows duplicates when paging on activities subpanel. |
||
412 | //If not for paging, no need to use rownumber() function |
||
413 | if ($count == 1 && $start == 0) |
||
414 | { |
||
415 | $limitUnionSQL = "SELECT TOP $count * FROM (" .$unionsql .") as top_count ".$unionOrderBy; |
||
416 | } |
||
417 | else |
||
418 | { |
||
419 | $limitUnionSQL = "SELECT TOP $count * FROM( select ROW_NUMBER() OVER ( order by " |
||
420 | .$rowNumOrderBy.") AS row_number, * FROM (" |
||
421 | .$unionsql .") As numbered) " |
||
422 | . "As top_count_limit WHERE row_number > $start " |
||
423 | .$unionOrderBy; |
||
424 | } |
||
425 | |||
426 | return $limitUnionSQL; |
||
427 | } |
||
428 | |||
429 | /** |
||
430 | * FIXME: verify and thoroughly test this code, these regexps look fishy |
||
431 | * @see DBManager::limitQuery() |
||
432 | */ |
||
433 | public function limitQuery($sql, $start, $count, $dieOnError = false, $msg = '', $execute = true) |
||
434 | { |
||
435 | $start = (int)$start; |
||
436 | $count = (int)$count; |
||
437 | $newSQL = $sql; |
||
438 | $distinctSQLARRAY = array(); |
||
439 | if (strpos($sql, "UNION") && !preg_match("/(')(UNION).?(')/i", $sql)) |
||
440 | $newSQL = $this->handleUnionLimitQuery($sql,$start,$count); |
||
441 | else { |
||
442 | if ($start < 0) |
||
443 | $start = 0; |
||
444 | $GLOBALS['log']->debug(print_r(func_get_args(),true)); |
||
445 | $this->lastsql = $sql; |
||
446 | $matches = array(); |
||
447 | preg_match('/^(.*SELECT\b)(.*?\bFROM\b.*\bWHERE\b)(.*)$/isU',$sql, $matches); |
||
448 | if (!empty($matches[3])) { |
||
449 | if ($start == 0) { |
||
450 | $match_two = strtolower($matches[2]); |
||
451 | if (!strpos($match_two, "distinct")> 0 && strpos($match_two, "distinct") !==0) { |
||
452 | $orderByMatch = array(); |
||
453 | preg_match('/^(.*)(\bORDER BY\b)(.*)$/is',$matches[3], $orderByMatch); |
||
454 | if (!empty($orderByMatch[3])) { |
||
455 | $selectPart = array(); |
||
456 | preg_match('/^(.*)(\bFROM\b.*)$/isU', $matches[2], $selectPart); |
||
457 | $newSQL = "SELECT TOP $count * FROM |
||
458 | ( |
||
459 | " . $matches[1] . $selectPart[1] . ", ROW_NUMBER() |
||
460 | OVER (ORDER BY " . $this->returnOrderBy($sql, $orderByMatch[3]) . ") AS row_number |
||
461 | " . $selectPart[2] . $orderByMatch[1]. " |
||
462 | ) AS a |
||
463 | WHERE row_number > $start"; |
||
464 | } |
||
465 | else { |
||
466 | $newSQL = $matches[1] . " TOP $count " . $matches[2] . $matches[3]; |
||
467 | } |
||
468 | } |
||
469 | else { |
||
470 | $distinct_o = strpos($match_two, "distinct"); |
||
471 | $up_to_distinct_str = substr($match_two, 0, $distinct_o); |
||
472 | //check to see if the distinct is within a function, if so, then proceed as normal |
||
473 | if (strpos($up_to_distinct_str,"(")) { |
||
474 | //proceed as normal |
||
475 | $newSQL = $matches[1] . " TOP $count " . $matches[2] . $matches[3]; |
||
476 | } |
||
477 | else { |
||
478 | //if distinct is not within a function, then parse |
||
479 | //string contains distinct clause, "TOP needs to come after Distinct" |
||
480 | //get position of distinct |
||
481 | $match_zero = strtolower($matches[0]); |
||
482 | $distinct_pos = strpos($match_zero , "distinct"); |
||
483 | //get position of where |
||
484 | $where_pos = strpos($match_zero, "where"); |
||
485 | //parse through string |
||
486 | $beg = substr($matches[0], 0, $distinct_pos+9 ); |
||
487 | $mid = substr($matches[0], strlen($beg), ($where_pos+5) - (strlen($beg))); |
||
488 | $end = substr($matches[0], strlen($beg) + strlen($mid) ); |
||
489 | //repopulate matches array |
||
490 | $matches[1] = $beg; $matches[2] = $mid; $matches[3] = $end; |
||
491 | |||
492 | $newSQL = $matches[1] . " TOP $count " . $matches[2] . $matches[3]; |
||
493 | } |
||
494 | } |
||
495 | } else { |
||
496 | $orderByMatch = array(); |
||
497 | preg_match('/^(.*)(\bORDER BY\b)(.*)$/is',$matches[3], $orderByMatch); |
||
498 | |||
499 | //if there is a distinct clause, parse sql string as we will have to insert the rownumber |
||
500 | //for paging, AFTER the distinct clause |
||
501 | $grpByStr = ''; |
||
502 | $hasDistinct = strpos(strtolower($matches[0]), "distinct"); |
||
503 | |||
504 | require_once('include/php-sql-parser.php'); |
||
505 | $parser = new PHPSQLParser(); |
||
506 | $sqlArray = $parser->parse($sql); |
||
507 | |||
508 | if ($hasDistinct) { |
||
509 | $matches_sql = strtolower($matches[0]); |
||
510 | //remove reference to distinct and select keywords, as we will use a group by instead |
||
511 | //we need to use group by because we are introducing rownumber column which would make every row unique |
||
512 | |||
513 | //take out the select and distinct from string so we can reuse in group by |
||
514 | $dist_str = 'distinct'; |
||
515 | preg_match('/\b' . $dist_str . '\b/simU', $matches_sql, $matchesPartSQL, PREG_OFFSET_CAPTURE); |
||
516 | $matches_sql = trim(substr($matches_sql,$matchesPartSQL[0][1] + strlen($dist_str))); |
||
517 | //get the position of where and from for further processing |
||
518 | preg_match('/\bfrom\b/simU', $matches_sql, $matchesPartSQL, PREG_OFFSET_CAPTURE); |
||
519 | $from_pos = $matchesPartSQL[0][1]; |
||
520 | preg_match('/\where\b/simU', $matches_sql, $matchesPartSQL, PREG_OFFSET_CAPTURE); |
||
521 | $where_pos = $matchesPartSQL[0][1]; |
||
522 | //split the sql into a string before and after the from clause |
||
523 | //we will use the columns being selected to construct the group by clause |
||
524 | if ($from_pos>0 ) { |
||
525 | $distinctSQLARRAY[0] = substr($matches_sql, 0, $from_pos); |
||
526 | $distinctSQLARRAY[1] = substr($matches_sql, $from_pos); |
||
527 | //get position of order by (if it exists) so we can strip it from the string |
||
528 | $ob_pos = strpos($distinctSQLARRAY[1], "order by"); |
||
529 | if ($ob_pos) { |
||
530 | $distinctSQLARRAY[1] = substr($distinctSQLARRAY[1],0,$ob_pos); |
||
531 | } |
||
532 | |||
533 | // strip off last closing parentheses from the where clause |
||
534 | $distinctSQLARRAY[1] = preg_replace('/\)\s$/',' ',$distinctSQLARRAY[1]); |
||
535 | } |
||
536 | |||
537 | $grpByStr = array(); |
||
538 | foreach ($sqlArray['SELECT'] as $record) { |
||
539 | if ($record['expr_type'] == 'const') { |
||
540 | continue; |
||
541 | } |
||
542 | $grpByStr[] = trim($record['base_expr']); |
||
543 | } |
||
544 | $grpByStr = implode(', ', $grpByStr); |
||
545 | } |
||
546 | |||
547 | if (!empty($orderByMatch[3])) { |
||
548 | //if there is a distinct clause, form query with rownumber after distinct |
||
549 | if ($hasDistinct) { |
||
550 | $newSQL = "SELECT TOP $count * FROM |
||
551 | ( |
||
552 | SELECT ROW_NUMBER() |
||
553 | OVER (ORDER BY " . preg_replace('/^' . $dist_str . '\s+/', '', $this->returnOrderBy($sql, $orderByMatch[3])) . ") AS row_number, |
||
554 | count(*) counter, " . $distinctSQLARRAY[0] . " |
||
555 | " . $distinctSQLARRAY[1] . " |
||
556 | group by " . $grpByStr . " |
||
557 | ) AS a |
||
558 | WHERE row_number > $start"; |
||
559 | } |
||
560 | else { |
||
561 | $newSQL = "SELECT TOP $count * FROM |
||
562 | ( |
||
563 | " . $matches[1] . " ROW_NUMBER() |
||
564 | OVER (ORDER BY " . $this->returnOrderBy($sql, $orderByMatch[3]) . ") AS row_number, |
||
565 | " . $matches[2] . $orderByMatch[1]. " |
||
566 | ) AS a |
||
567 | WHERE row_number > $start"; |
||
568 | } |
||
569 | }else{ |
||
570 | //if there is a distinct clause, form query with rownumber after distinct |
||
571 | if ($hasDistinct) { |
||
572 | $newSQL = "SELECT TOP $count * FROM |
||
573 | ( |
||
574 | SELECT ROW_NUMBER() OVER (ORDER BY ".$grpByStr.") AS row_number, count(*) counter, " . $distinctSQLARRAY[0] . " |
||
575 | " . $distinctSQLARRAY[1] . " |
||
576 | group by " . $grpByStr . " |
||
577 | ) |
||
578 | AS a |
||
579 | WHERE row_number > $start"; |
||
580 | } |
||
581 | else { |
||
582 | $newSQL = "SELECT TOP $count * FROM |
||
583 | ( |
||
584 | " . $matches[1] . " ROW_NUMBER() OVER (ORDER BY " . $sqlArray['FROM'][0]['alias'] . ".id) AS row_number, " . $matches[2] . $matches[3]. " |
||
585 | ) |
||
586 | AS a |
||
587 | WHERE row_number > $start"; |
||
588 | } |
||
589 | } |
||
590 | } |
||
591 | } |
||
592 | } |
||
593 | |||
594 | $GLOBALS['log']->debug('Limit Query: ' . $newSQL); |
||
595 | if($execute) { |
||
596 | $result = $this->query($newSQL, $dieOnError, $msg); |
||
597 | $this->dump_slow_queries($newSQL); |
||
598 | return $result; |
||
599 | } else { |
||
600 | return $newSQL; |
||
601 | } |
||
602 | } |
||
603 | |||
604 | |||
605 | /** |
||
606 | * Searches for begginning and ending characters. It places contents into |
||
607 | * an array and replaces contents in original string. This is used to account for use of |
||
608 | * nested functions while aliasing column names |
||
609 | * |
||
610 | * @param string $p_sql SQL statement |
||
611 | * @param string $strip_beg Beginning character |
||
612 | * @param string $strip_end Ending character |
||
613 | * @param string $patt Optional, pattern to |
||
614 | */ |
||
615 | private function removePatternFromSQL($p_sql, $strip_beg, $strip_end, $patt = 'patt') |
||
616 | { |
||
617 | //strip all single quotes out |
||
618 | $count = substr_count ( $p_sql, $strip_beg); |
||
619 | $increment = 1; |
||
620 | if ($strip_beg != $strip_end) |
||
621 | $increment = 2; |
||
622 | |||
623 | $i=0; |
||
624 | $offset = 0; |
||
625 | $strip_array = array(); |
||
626 | while ($i<$count && $offset<strlen($p_sql)) { |
||
627 | if ($offset > strlen($p_sql)) |
||
628 | { |
||
629 | break; |
||
630 | } |
||
631 | |||
632 | $beg_sin = strpos($p_sql, $strip_beg, $offset); |
||
633 | if (!$beg_sin) |
||
634 | { |
||
635 | break; |
||
636 | } |
||
637 | $sec_sin = strpos($p_sql, $strip_end, $beg_sin+1); |
||
638 | $strip_array[$patt.$i] = substr($p_sql, $beg_sin, $sec_sin - $beg_sin +1); |
||
639 | if ($increment > 1) { |
||
640 | //we are in here because beginning and end patterns are not identical, so search for nesting |
||
641 | $exists = strpos($strip_array[$patt.$i], $strip_beg ); |
||
642 | if ($exists>=0) { |
||
643 | $nested_pos = (strrpos($strip_array[$patt.$i], $strip_beg )); |
||
644 | $strip_array[$patt.$i] = substr($p_sql,$nested_pos+$beg_sin,$sec_sin - ($nested_pos+$beg_sin)+1); |
||
645 | $p_sql = substr($p_sql, 0, $nested_pos+$beg_sin) . " ##". $patt.$i."## " . substr($p_sql, $sec_sin+1); |
||
646 | $i = $i + 1; |
||
647 | continue; |
||
648 | } |
||
649 | } |
||
650 | $p_len = strlen("##". $patt.$i."##"); |
||
651 | $p_sql = substr($p_sql, 0, $beg_sin) . " ##". $patt.$i."## " . substr($p_sql, $sec_sin+1); |
||
652 | //move the marker up |
||
653 | $offset = ($sec_sin-($sec_sin-$beg_sin))+$p_len+1; // Adjusting the starting point of the marker |
||
654 | |||
655 | $i = $i + 1; |
||
656 | } |
||
657 | $strip_array['sql_string'] = $p_sql; |
||
658 | |||
659 | return $strip_array; |
||
660 | } |
||
661 | |||
662 | /** |
||
663 | * adds a pattern |
||
664 | * |
||
665 | * @param string $token |
||
666 | * @param array $pattern_array |
||
667 | * @return string |
||
668 | */ |
||
669 | private function addPatternToSQL($token, array $pattern_array) |
||
670 | { |
||
671 | //strip all single quotes out |
||
672 | $pattern_array = array_reverse($pattern_array); |
||
673 | |||
674 | foreach ($pattern_array as $key => $replace) { |
||
675 | $token = str_replace( " ##".$key."## ", $replace,$token); |
||
676 | } |
||
677 | |||
678 | return $token; |
||
679 | } |
||
680 | |||
681 | /** |
||
682 | * gets an alias from the sql statement |
||
683 | * |
||
684 | * @param string $sql |
||
685 | * @param string $alias |
||
686 | * @return string |
||
687 | */ |
||
688 | private function getAliasFromSQL($sql, $alias) |
||
689 | { |
||
690 | $matches = array(); |
||
691 | preg_match('/^(.*SELECT)(.*?FROM.*WHERE)(.*)$/isU',$sql, $matches); |
||
692 | //parse all single and double quotes out of array |
||
693 | $sin_array = $this->removePatternFromSQL($matches[2], "'", "'","sin_"); |
||
694 | $new_sql = array_pop($sin_array); |
||
695 | $dub_array = $this->removePatternFromSQL($new_sql, "\"", "\"","dub_"); |
||
696 | $new_sql = array_pop($dub_array); |
||
697 | |||
698 | //search for parenthesis |
||
699 | $paren_array = $this->removePatternFromSQL($new_sql, "(", ")", "par_"); |
||
700 | $new_sql = array_pop($paren_array); |
||
701 | |||
702 | //all functions should be removed now, so split the array on commas |
||
703 | $mstr_sql_array = explode(",", $new_sql); |
||
704 | foreach($mstr_sql_array as $token ) { |
||
705 | if (strpos($token, $alias)) { |
||
706 | //found token, add back comments |
||
707 | $token = $this->addPatternToSQL($token, $paren_array); |
||
708 | $token = $this->addPatternToSQL($token, $dub_array); |
||
709 | $token = $this->addPatternToSQL($token, $sin_array); |
||
710 | |||
711 | //log and break out of this function |
||
712 | return $token; |
||
713 | } |
||
714 | } |
||
715 | return null; |
||
716 | } |
||
717 | |||
718 | |||
719 | /** |
||
720 | * Finds the alias of the order by column, and then return the preceding column name |
||
721 | * |
||
722 | * @param string $sql |
||
723 | * @param string $orderMatch |
||
724 | * @return string |
||
725 | */ |
||
726 | private function findColumnByAlias($sql, $orderMatch) |
||
727 | { |
||
728 | //change case to lowercase |
||
729 | $sql = strtolower($sql); |
||
730 | $patt = '/\s+'.trim($orderMatch).'\s*(,|from)/'; |
||
731 | |||
732 | //check for the alias, it should contain comma, may contain space, \n, or \t |
||
733 | $matches = array(); |
||
734 | preg_match($patt, $sql, $matches, PREG_OFFSET_CAPTURE); |
||
735 | $found_in_sql = isset($matches[0][1]) ? $matches[0][1] : false; |
||
736 | |||
737 | |||
738 | //set default for found variable |
||
739 | $found = $found_in_sql; |
||
740 | |||
741 | //if still no match found, then we need to parse through the string |
||
742 | if (!$found_in_sql){ |
||
0 ignored issues
–
show
The expression
$found_in_sql of type integer|false is loosely compared to false ; this is ambiguous if the integer can be zero. You might want to explicitly use === null instead.
In PHP, under loose comparison (like For 0 == false // true
0 == null // true
123 == false // false
123 == null // false
// It is often better to use strict comparison
0 === false // false
0 === null // false
![]() |
|||
743 | //get count of how many times the match exists in string |
||
744 | $found_count = substr_count($sql, $orderMatch); |
||
745 | $i = 0; |
||
746 | $first_ = 0; |
||
747 | $len = strlen($orderMatch); |
||
748 | //loop through string as many times as there is a match |
||
749 | while ($found_count > $i) { |
||
750 | //get the first match |
||
751 | $found_in_sql = strpos($sql, $orderMatch,$first_); |
||
752 | //make sure there was a match |
||
753 | if($found_in_sql){ |
||
754 | //grab the next 2 individual characters |
||
755 | $str_plusone = substr($sql,$found_in_sql + $len,1); |
||
756 | $str_plustwo = substr($sql,$found_in_sql + $len+1,1); |
||
757 | //if one of those characters is a comma, then we have our alias |
||
758 | if ($str_plusone === "," || $str_plustwo === ","){ |
||
759 | //keep track of this position |
||
760 | $found = $found_in_sql; |
||
761 | } |
||
762 | } |
||
763 | //set the offset and increase the iteration counter |
||
764 | $first_ = $found_in_sql+$len; |
||
765 | $i = $i+1; |
||
766 | } |
||
767 | } |
||
768 | //return $found, defaults have been set, so if no match was found it will be a negative number |
||
769 | return $found; |
||
770 | } |
||
771 | |||
772 | |||
773 | /** |
||
774 | * Return the order by string to use in case the column has been aliased |
||
775 | * |
||
776 | * @param string $sql |
||
777 | * @param string $orig_order_match |
||
778 | * @return string |
||
779 | */ |
||
780 | private function returnOrderBy($sql, $orig_order_match) |
||
781 | { |
||
782 | $sql = strtolower($sql); |
||
783 | $orig_order_match = trim($orig_order_match); |
||
784 | if (strpos($orig_order_match, ".") != 0) |
||
785 | //this has a tablename defined, pass in the order match |
||
786 | return $orig_order_match; |
||
787 | |||
788 | // If there is no ordering direction (ASC/DESC), use ASC by default |
||
789 | if (strpos($orig_order_match, " ") === false) { |
||
790 | $orig_order_match .= " ASC"; |
||
791 | } |
||
792 | |||
793 | //grab first space in order by |
||
794 | $firstSpace = strpos($orig_order_match, " "); |
||
795 | |||
796 | //split order by into column name and ascending/descending |
||
797 | $orderMatch = " " . strtolower(substr($orig_order_match, 0, $firstSpace)); |
||
798 | $asc_desc = trim(substr($orig_order_match,$firstSpace)); |
||
799 | |||
800 | //look for column name as an alias in sql string |
||
801 | $found_in_sql = $this->findColumnByAlias($sql, $orderMatch); |
||
802 | |||
803 | if (!$found_in_sql) { |
||
0 ignored issues
–
show
The expression
$found_in_sql of type integer|false is loosely compared to false ; this is ambiguous if the integer can be zero. You might want to explicitly use === null instead.
In PHP, under loose comparison (like For 0 == false // true
0 == null // true
123 == false // false
123 == null // false
// It is often better to use strict comparison
0 === false // false
0 === null // false
![]() |
|||
804 | //check if this column needs the tablename prefixed to it |
||
805 | $orderMatch = ".".trim($orderMatch); |
||
806 | $colMatchPos = strpos($sql, $orderMatch); |
||
807 | if ($colMatchPos !== false) { |
||
808 | //grab sub string up to column name |
||
809 | $containsColStr = substr($sql,0, $colMatchPos); |
||
810 | //get position of first space, so we can grab table name |
||
811 | $lastSpacePos = strrpos($containsColStr, " "); |
||
812 | //use positions of column name, space before name, and length of column to find the correct column name |
||
813 | $col_name = substr($sql, $lastSpacePos, $colMatchPos-$lastSpacePos+strlen($orderMatch)); |
||
814 | //bug 25485. When sorting by a custom field in Account List and then pressing NEXT >, system gives an error |
||
815 | $containsCommaPos = strpos($col_name, ","); |
||
816 | if($containsCommaPos !== false) { |
||
817 | $col_name = substr($col_name, $containsCommaPos+1); |
||
818 | } |
||
819 | //add the "asc/desc" order back |
||
820 | $col_name = $col_name. " ". $asc_desc; |
||
821 | |||
822 | //return column name |
||
823 | return $col_name; |
||
824 | } |
||
825 | //break out of here, log this |
||
826 | $GLOBALS['log']->debug("No match was found for order by, pass string back untouched as: $orig_order_match"); |
||
827 | return $orig_order_match; |
||
828 | } |
||
829 | else { |
||
830 | //if found, then parse and return |
||
831 | //grab string up to the aliased column |
||
832 | $GLOBALS['log']->debug("order by found, process sql string"); |
||
833 | |||
834 | $psql = (trim($this->getAliasFromSQL($sql, $orderMatch ))); |
||
835 | if (empty($psql)) |
||
836 | $psql = trim(substr($sql, 0, $found_in_sql)); |
||
837 | |||
838 | //grab the last comma before the alias |
||
839 | preg_match('/\s+' . trim($orderMatch). '/', $psql, $match, PREG_OFFSET_CAPTURE); |
||
840 | $comma_pos = $match[0][1]; |
||
841 | //substring between the comma and the alias to find the joined_table alias and column name |
||
842 | $col_name = substr($psql,0, $comma_pos); |
||
843 | |||
844 | //make sure the string does not have an end parenthesis |
||
845 | //and is not part of a function (i.e. "ISNULL(leads.last_name,'') as name" ) |
||
846 | //this is especially true for unified search from home screen |
||
847 | |||
848 | $alias_beg_pos = 0; |
||
849 | if(strpos($psql, " as ")) |
||
850 | $alias_beg_pos = strpos($psql, " as "); |
||
851 | |||
852 | // Bug # 44923 - This breaks the query and does not properly filter isnull |
||
853 | // as there are other functions such as ltrim and rtrim. |
||
854 | /* else if (strncasecmp($psql, 'isnull', 6) != 0) |
||
855 | $alias_beg_pos = strpos($psql, " "); */ |
||
856 | |||
857 | if ($alias_beg_pos > 0) { |
||
858 | $col_name = substr($psql,0, $alias_beg_pos ); |
||
859 | } |
||
860 | //add the "asc/desc" order back |
||
861 | $col_name = $col_name. " ". $asc_desc; |
||
862 | |||
863 | //pass in new order by |
||
864 | $GLOBALS['log']->debug("order by being returned is " . $col_name); |
||
865 | return $col_name; |
||
866 | } |
||
867 | } |
||
868 | |||
869 | /** |
||
870 | * Take in a string of the module and retrieve the correspondent table name |
||
871 | * |
||
872 | * @param string $module_str module name |
||
873 | * @param string $sql SQL statement |
||
874 | * @return string table name |
||
875 | */ |
||
876 | private function getTableNameFromModuleName($module_str, $sql) |
||
877 | { |
||
878 | |||
879 | global $beanList, $beanFiles; |
||
880 | $GLOBALS['log']->debug("Module being processed is " . $module_str); |
||
881 | //get the right module files |
||
882 | //the module string exists in bean list, then process bean for correct table name |
||
883 | //note that we exempt the reports module from this, as queries from reporting module should be parsed for |
||
884 | //correct table name. |
||
885 | if (($module_str != 'Reports' && $module_str != 'SavedReport') && isset($beanList[$module_str]) && isset($beanFiles[$beanList[$module_str]])){ |
||
886 | //if the class is not already loaded, then load files |
||
887 | if (!class_exists($beanList[$module_str])) |
||
888 | require_once($beanFiles[$beanList[$module_str]]); |
||
889 | |||
890 | //instantiate new bean |
||
891 | $module_bean = new $beanList[$module_str](); |
||
892 | //get table name from bean |
||
893 | $tbl_name = $module_bean->table_name; |
||
894 | //make sure table name is not just a blank space, or empty |
||
895 | $tbl_name = trim($tbl_name); |
||
896 | |||
897 | if(empty($tbl_name)){ |
||
898 | $GLOBALS['log']->debug("Could not find table name for module $module_str. "); |
||
899 | $tbl_name = $module_str; |
||
900 | } |
||
901 | } |
||
902 | else { |
||
903 | //since the module does NOT exist in beanlist, then we have to parse the string |
||
904 | //and grab the table name from the passed in sql |
||
905 | $GLOBALS['log']->debug("Could not find table name from module in request, retrieve from passed in sql"); |
||
906 | $tbl_name = $module_str; |
||
907 | $sql = strtolower($sql); |
||
908 | |||
909 | // Bug #45625 : Getting Multi-part identifier (reports.id) could not be bound error when navigating to next page in reprots in mssql |
||
910 | // there is cases when sql string is multiline string and it we cannot find " from " string in it |
||
911 | $sql = str_replace(array("\n", "\r"), " ", $sql); |
||
912 | |||
913 | //look for the location of the "from" in sql string |
||
914 | $fromLoc = strpos($sql," from " ); |
||
915 | if ($fromLoc>0){ |
||
916 | //found from, substring from the " FROM " string in sql to end |
||
917 | $tableEnd = substr($sql, $fromLoc+6); |
||
918 | //We know that tablename will be next parameter after from, so |
||
919 | //grab the next space after table name. |
||
920 | // MFH BUG #14009: Also check to see if there are any carriage returns before the next space so that we don't grab any arbitrary joins or other tables. |
||
921 | $carriage_ret = strpos($tableEnd,"\n"); |
||
922 | $next_space = strpos($tableEnd," " ); |
||
923 | if ($carriage_ret < $next_space) |
||
924 | $next_space = $carriage_ret; |
||
925 | if ($next_space > 0) { |
||
926 | $tbl_name= substr($tableEnd,0, $next_space); |
||
927 | if(empty($tbl_name)){ |
||
928 | $GLOBALS['log']->debug("Could not find table name sql either, return $module_str. "); |
||
929 | $tbl_name = $module_str; |
||
930 | } |
||
931 | } |
||
932 | |||
933 | //grab the table, to see if it is aliased |
||
934 | $aliasTableEnd = trim(substr($tableEnd, $next_space)); |
||
935 | $alias_space = strpos ($aliasTableEnd, " " ); |
||
936 | if ($alias_space > 0){ |
||
937 | $alias_tbl_name= substr($aliasTableEnd,0, $alias_space); |
||
938 | strtolower($alias_tbl_name); |
||
939 | if(empty($alias_tbl_name) |
||
0 ignored issues
–
show
This
if statement is empty and can be removed.
This check looks for the bodies of These if (rand(1, 6) > 3) {
//print "Check failed";
} else {
print "Check succeeded";
}
could be turned into if (rand(1, 6) <= 3) {
print "Check succeeded";
}
This is much more concise to read. ![]() |
|||
940 | || $alias_tbl_name == "where" |
||
941 | || $alias_tbl_name == "inner" |
||
942 | || $alias_tbl_name == "left" |
||
943 | || $alias_tbl_name == "join" |
||
944 | || $alias_tbl_name == "outer" |
||
945 | || $alias_tbl_name == "right") { |
||
946 | //not aliased, do nothing |
||
947 | } |
||
948 | elseif ($alias_tbl_name == "as") { |
||
949 | //the next word is the table name |
||
950 | $aliasTableEnd = trim(substr($aliasTableEnd, $alias_space)); |
||
951 | $alias_space = strpos ($aliasTableEnd, " " ); |
||
952 | if ($alias_space > 0) { |
||
953 | $alias_tbl_name= trim(substr($aliasTableEnd,0, $alias_space)); |
||
954 | if (!empty($alias_tbl_name)) |
||
955 | $tbl_name = $alias_tbl_name; |
||
956 | } |
||
957 | } |
||
958 | else { |
||
959 | //this is table alias |
||
960 | $tbl_name = $alias_tbl_name; |
||
961 | } |
||
962 | } |
||
963 | } |
||
964 | } |
||
965 | //return table name |
||
966 | $GLOBALS['log']->debug("Table name for module $module_str is: ".$tbl_name); |
||
967 | return $tbl_name; |
||
968 | } |
||
969 | |||
970 | |||
971 | /** |
||
972 | * @see DBManager::getFieldsArray() |
||
973 | */ |
||
974 | public function getFieldsArray($result, $make_lower_case = false) |
||
975 | { |
||
976 | $field_array = array(); |
||
977 | |||
978 | if(! isset($result) || empty($result)) |
||
979 | return 0; |
||
980 | |||
981 | $i = 0; |
||
982 | while ($i < mssql_num_fields($result)) { |
||
983 | $meta = mssql_fetch_field($result, $i); |
||
984 | if (!$meta) |
||
985 | return 0; |
||
986 | if($make_lower_case==true) |
||
0 ignored issues
–
show
|
|||
987 | $meta->name = strtolower($meta->name); |
||
988 | |||
989 | $field_array[] = $meta->name; |
||
990 | |||
991 | $i++; |
||
992 | } |
||
993 | |||
994 | return $field_array; |
||
995 | } |
||
996 | |||
997 | /** |
||
998 | * @see DBManager::getAffectedRowCount() |
||
999 | */ |
||
1000 | public function getAffectedRowCount($result) |
||
1001 | { |
||
1002 | return $this->getOne("SELECT @@ROWCOUNT"); |
||
1003 | } |
||
1004 | |||
1005 | /** |
||
1006 | * @see DBManager::fetchRow() |
||
1007 | */ |
||
1008 | public function fetchRow($result) |
||
1009 | { |
||
1010 | if (empty($result)) return false; |
||
1011 | |||
1012 | $row = mssql_fetch_assoc($result); |
||
1013 | //MSSQL returns a space " " when a varchar column is empty ("") and not null. |
||
1014 | //We need to iterate through the returned row array and strip empty spaces |
||
1015 | if(!empty($row)){ |
||
1016 | foreach($row as $key => $column) { |
||
1017 | //notice we only strip if one space is returned. we do not want to strip |
||
1018 | //strings with intentional spaces (" foo ") |
||
1019 | if (!empty($column) && $column ==" ") { |
||
1020 | $row[$key] = ''; |
||
1021 | } |
||
1022 | } |
||
1023 | } |
||
1024 | return $row; |
||
1025 | } |
||
1026 | |||
1027 | /** |
||
1028 | * @see DBManager::quote() |
||
1029 | */ |
||
1030 | public function quote($string) |
||
1031 | { |
||
1032 | if(is_array($string)) { |
||
1033 | return $this->arrayQuote($string); |
||
1034 | } |
||
1035 | return str_replace("'","''", $this->quoteInternal($string)); |
||
1036 | } |
||
1037 | |||
1038 | /** |
||
1039 | * @see DBManager::quoteIdentifier() |
||
1040 | */ |
||
1041 | public function quoteIdentifier($string) |
||
1042 | { |
||
1043 | return '['.$string.']'; |
||
1044 | } |
||
1045 | |||
1046 | /** |
||
1047 | * @see DBManager::tableExists() |
||
1048 | */ |
||
1049 | public function tableExists($tableName) |
||
1050 | { |
||
1051 | $GLOBALS['log']->info("tableExists: $tableName"); |
||
1052 | |||
1053 | $this->checkConnection(); |
||
1054 | $result = $this->getOne( |
||
1055 | "SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE='BASE TABLE' AND TABLE_NAME=".$this->quoted($tableName)); |
||
1056 | |||
1057 | return !empty($result); |
||
1058 | } |
||
1059 | |||
1060 | /** |
||
1061 | * Get tables like expression |
||
1062 | * @param $like string |
||
1063 | * @return array |
||
1064 | */ |
||
1065 | public function tablesLike($like) |
||
1066 | { |
||
1067 | if ($this->getDatabase()) { |
||
1068 | $tables = array(); |
||
1069 | $r = $this->query('SELECT TABLE_NAME tn FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE=\'BASE TABLE\' AND TABLE_NAME LIKE '.$this->quoted($like)); |
||
1070 | if (!empty($r)) { |
||
1071 | while ($a = $this->fetchByAssoc($r)) { |
||
1072 | $row = array_values($a); |
||
1073 | $tables[]=$row[0]; |
||
1074 | } |
||
1075 | return $tables; |
||
1076 | } |
||
1077 | } |
||
1078 | return false; |
||
1079 | } |
||
1080 | |||
1081 | /** |
||
1082 | * @see DBManager::getTablesArray() |
||
1083 | */ |
||
1084 | public function getTablesArray() |
||
1085 | { |
||
1086 | $GLOBALS['log']->debug('MSSQL fetching table list'); |
||
1087 | |||
1088 | if($this->getDatabase()) { |
||
1089 | $tables = array(); |
||
1090 | $r = $this->query('SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES'); |
||
1091 | if (is_resource($r)) { |
||
1092 | while ($a = $this->fetchByAssoc($r)) |
||
1093 | $tables[] = $a['TABLE_NAME']; |
||
1094 | |||
1095 | return $tables; |
||
1096 | } |
||
1097 | } |
||
1098 | |||
1099 | return false; // no database available |
||
1100 | } |
||
1101 | |||
1102 | |||
1103 | /** |
||
1104 | * This call is meant to be used during install, when Full Text Search is enabled |
||
1105 | * Indexing would always occur after a fresh sql server install, so this code creates |
||
1106 | * a catalog and table with full text index. |
||
1107 | */ |
||
1108 | public function full_text_indexing_setup() |
||
1109 | { |
||
1110 | $GLOBALS['log']->debug('MSSQL about to wakeup FTS'); |
||
1111 | |||
1112 | if($this->getDatabase()) { |
||
1113 | //create wakeup catalog |
||
1114 | $FTSqry[] = "if not exists( select * from sys.fulltext_catalogs where name ='wakeup_catalog' ) |
||
1115 | CREATE FULLTEXT CATALOG wakeup_catalog |
||
1116 | "; |
||
1117 | |||
1118 | //drop wakeup table if it exists |
||
1119 | $FTSqry[] = "IF EXISTS(SELECT 'fts_wakeup' FROM sysobjects WHERE name = 'fts_wakeup' AND xtype='U') |
||
1120 | DROP TABLE fts_wakeup |
||
1121 | "; |
||
1122 | //create wakeup table |
||
1123 | $FTSqry[] = "CREATE TABLE fts_wakeup( |
||
1124 | id varchar(36) NOT NULL CONSTRAINT pk_fts_wakeup_id PRIMARY KEY CLUSTERED (id ASC ), |
||
1125 | body text NULL, |
||
1126 | kb_index int IDENTITY(1,1) NOT NULL CONSTRAINT wakeup_fts_unique_idx UNIQUE NONCLUSTERED |
||
1127 | ) |
||
1128 | "; |
||
1129 | //create full text index |
||
1130 | $FTSqry[] = "CREATE FULLTEXT INDEX ON fts_wakeup |
||
1131 | ( |
||
1132 | body |
||
1133 | Language 0X0 |
||
1134 | ) |
||
1135 | KEY INDEX wakeup_fts_unique_idx ON wakeup_catalog |
||
1136 | WITH CHANGE_TRACKING AUTO |
||
1137 | "; |
||
1138 | |||
1139 | //insert dummy data |
||
1140 | $FTSqry[] = "INSERT INTO fts_wakeup (id ,body) |
||
1141 | VALUES ('".create_guid()."', 'SugarCRM Rocks' )"; |
||
1142 | |||
1143 | |||
1144 | //create queries to stop and restart indexing |
||
1145 | $FTSqry[] = 'ALTER FULLTEXT INDEX ON fts_wakeup STOP POPULATION'; |
||
1146 | $FTSqry[] = 'ALTER FULLTEXT INDEX ON fts_wakeup DISABLE'; |
||
1147 | $FTSqry[] = 'ALTER FULLTEXT INDEX ON fts_wakeup ENABLE'; |
||
1148 | $FTSqry[] = 'ALTER FULLTEXT INDEX ON fts_wakeup SET CHANGE_TRACKING MANUAL'; |
||
1149 | $FTSqry[] = 'ALTER FULLTEXT INDEX ON fts_wakeup START FULL POPULATION'; |
||
1150 | $FTSqry[] = 'ALTER FULLTEXT INDEX ON fts_wakeup SET CHANGE_TRACKING AUTO'; |
||
1151 | |||
1152 | foreach($FTSqry as $q){ |
||
1153 | sleep(3); |
||
1154 | $this->query($q); |
||
1155 | } |
||
1156 | $this->create_default_full_text_catalog(); |
||
1157 | } |
||
1158 | |||
1159 | return false; // no database available |
||
1160 | } |
||
1161 | |||
1162 | protected $date_formats = array( |
||
1163 | '%Y-%m-%d' => 10, |
||
1164 | '%Y-%m' => 7, |
||
1165 | '%Y' => 4, |
||
1166 | ); |
||
1167 | |||
1168 | /** |
||
1169 | * @see DBManager::convert() |
||
1170 | */ |
||
1171 | public function convert($string, $type, array $additional_parameters = array()) |
||
1172 | { |
||
1173 | // convert the parameters array into a comma delimited string |
||
1174 | if (!empty($additional_parameters)) { |
||
1175 | $additional_parameters_string = ','.implode(',',$additional_parameters); |
||
1176 | } else { |
||
1177 | $additional_parameters_string = ''; |
||
1178 | } |
||
1179 | $all_parameters = $additional_parameters; |
||
1180 | if(is_array($string)) { |
||
1181 | $all_parameters = array_merge($string, $all_parameters); |
||
1182 | } elseif (!is_null($string)) { |
||
1183 | array_unshift($all_parameters, $string); |
||
1184 | } |
||
1185 | |||
1186 | switch (strtolower($type)) { |
||
1187 | case 'today': |
||
1188 | return "GETDATE()"; |
||
1189 | case 'left': |
||
1190 | return "LEFT($string$additional_parameters_string)"; |
||
1191 | case 'date_format': |
||
0 ignored issues
–
show
|
|||
1192 | if(!empty($additional_parameters[0]) && $additional_parameters[0][0] == "'") { |
||
1193 | $additional_parameters[0] = trim($additional_parameters[0], "'"); |
||
1194 | } |
||
1195 | if(!empty($additional_parameters) && isset($this->date_formats[$additional_parameters[0]])) { |
||
1196 | $len = $this->date_formats[$additional_parameters[0]]; |
||
1197 | return "LEFT(CONVERT(varchar($len),". $string . ",120),$len)"; |
||
1198 | } else { |
||
1199 | return "LEFT(CONVERT(varchar(10),". $string . ",120),10)"; |
||
1200 | } |
||
1201 | case 'ifnull': |
||
1202 | if(empty($additional_parameters_string)) { |
||
1203 | $additional_parameters_string = ",''"; |
||
1204 | } |
||
1205 | return "ISNULL($string$additional_parameters_string)"; |
||
1206 | case 'concat': |
||
1207 | return implode("+",$all_parameters); |
||
1208 | case 'text2char': |
||
1209 | return "CAST($string AS varchar(8000))"; |
||
1210 | case 'quarter': |
||
1211 | return "DATENAME(quarter, $string)"; |
||
1212 | case "length": |
||
1213 | return "LEN($string)"; |
||
1214 | case 'month': |
||
1215 | return "MONTH($string)"; |
||
1216 | case 'add_date': |
||
1217 | return "DATEADD({$additional_parameters[1]},{$additional_parameters[0]},$string)"; |
||
1218 | case 'add_time': |
||
1219 | return "DATEADD(hh, {$additional_parameters[0]}, DATEADD(mi, {$additional_parameters[1]}, $string))"; |
||
1220 | case 'add_tz_offset' : |
||
0 ignored issues
–
show
There must be no space before the colon in a CASE statement
As per the PSR-2 coding standard, there must not be a space in front of the colon in case statements. switch ($selector) {
case "A": //right
doSomething();
break;
case "B" : //wrong
doSomethingElse();
break;
}
To learn more about the PSR-2 coding standard, please refer to the PHP-Fig. ![]() |
|||
1221 | $getUserUTCOffset = $GLOBALS['timedate']->getUserUTCOffset(); |
||
1222 | $operation = $getUserUTCOffset < 0 ? '-' : '+'; |
||
1223 | return 'DATEADD(minute, ' . $operation . abs($getUserUTCOffset) . ', ' . $string. ')'; |
||
1224 | case 'avg': |
||
1225 | return "avg($string)"; |
||
1226 | } |
||
1227 | |||
1228 | return "$string"; |
||
1229 | } |
||
1230 | |||
1231 | /** |
||
1232 | * @see DBManager::fromConvert() |
||
1233 | */ |
||
1234 | public function fromConvert($string, $type) |
||
1235 | { |
||
1236 | switch($type) { |
||
1237 | case 'datetimecombo': |
||
1238 | case 'datetime': return substr($string, 0,19); |
||
0 ignored issues
–
show
The case body in a switch statement must start on the line following the statement.
According to the PSR-2, the body of a case statement must start on the line immediately following the case statement. switch ($expr) {
case "A":
doSomething(); //right
break;
case "B":
doSomethingElse(); //wrong
break;
} To learn more about the PSR-2 coding standard, please refer to the PHP-Fig. ![]() Terminating statement must be on a line by itself
As per the PSR-2 coding standard, the switch ($expr) {
case "A":
doSomething();
break; //wrong
case "B":
doSomething();
break; //right
case "C:":
doSomething();
return true; //right
}
To learn more about the PSR-2 coding standard, please refer to the PHP-Fig. ![]() |
|||
1239 | case 'date': return substr($string, 0, 10); |
||
0 ignored issues
–
show
The case body in a switch statement must start on the line following the statement.
According to the PSR-2, the body of a case statement must start on the line immediately following the case statement. switch ($expr) {
case "A":
doSomething(); //right
break;
case "B":
doSomethingElse(); //wrong
break;
} To learn more about the PSR-2 coding standard, please refer to the PHP-Fig. ![]() Terminating statement must be on a line by itself
As per the PSR-2 coding standard, the switch ($expr) {
case "A":
doSomething();
break; //wrong
case "B":
doSomething();
break; //right
case "C:":
doSomething();
return true; //right
}
To learn more about the PSR-2 coding standard, please refer to the PHP-Fig. ![]() |
|||
1240 | case 'time': return substr($string, 11); |
||
0 ignored issues
–
show
The case body in a switch statement must start on the line following the statement.
According to the PSR-2, the body of a case statement must start on the line immediately following the case statement. switch ($expr) {
case "A":
doSomething(); //right
break;
case "B":
doSomethingElse(); //wrong
break;
} To learn more about the PSR-2 coding standard, please refer to the PHP-Fig. ![]() Terminating statement must be on a line by itself
As per the PSR-2 coding standard, the switch ($expr) {
case "A":
doSomething();
break; //wrong
case "B":
doSomething();
break; //right
case "C:":
doSomething();
return true; //right
}
To learn more about the PSR-2 coding standard, please refer to the PHP-Fig. ![]() |
|||
1241 | } |
||
1242 | return $string; |
||
1243 | } |
||
1244 | |||
1245 | /** |
||
1246 | * @see DBManager::createTableSQLParams() |
||
1247 | */ |
||
1248 | public function createTableSQLParams($tablename, $fieldDefs, $indices) |
||
1249 | { |
||
1250 | if (empty($tablename) || empty($fieldDefs)) |
||
1251 | return ''; |
||
1252 | |||
1253 | $columns = $this->columnSQLRep($fieldDefs, false, $tablename); |
||
1254 | if (empty($columns)) |
||
1255 | return ''; |
||
1256 | |||
1257 | return "CREATE TABLE $tablename ($columns)"; |
||
1258 | } |
||
1259 | |||
1260 | /** |
||
1261 | * Does this type represent text (i.e., non-varchar) value? |
||
1262 | * @param string $type |
||
1263 | */ |
||
1264 | public function isTextType($type) |
||
1265 | { |
||
1266 | $type = strtolower($type); |
||
1267 | if(!isset($this->type_map[$type])) return false; |
||
1268 | return in_array($this->type_map[$type], array('ntext','text','image', 'nvarchar(max)')); |
||
1269 | } |
||
1270 | |||
1271 | /** |
||
1272 | * Return representation of an empty value depending on type |
||
1273 | * @param string $type |
||
1274 | */ |
||
1275 | public function emptyValue($type) |
||
1276 | { |
||
1277 | $ctype = $this->getColumnType($type); |
||
1278 | if($ctype == "datetime") { |
||
1279 | return $this->convert($this->quoted("1970-01-01 00:00:00"), "datetime"); |
||
1280 | } |
||
1281 | if($ctype == "date") { |
||
1282 | return $this->convert($this->quoted("1970-01-01"), "datetime"); |
||
1283 | } |
||
1284 | if($ctype == "time") { |
||
1285 | return $this->convert($this->quoted("00:00:00"), "time"); |
||
1286 | } |
||
1287 | return parent::emptyValue($type); |
||
1288 | } |
||
1289 | |||
1290 | public function renameColumnSQL($tablename, $column, $newname) |
||
1291 | { |
||
1292 | return "SP_RENAME '$tablename.$column', '$newname', 'COLUMN'"; |
||
1293 | } |
||
1294 | |||
1295 | /** |
||
1296 | * Returns the SQL Alter table statment |
||
1297 | * |
||
1298 | * MSSQL has a quirky T-SQL alter table syntax. Pay special attention to the |
||
1299 | * modify operation |
||
1300 | * @param string $action |
||
1301 | * @param array $def |
||
1302 | * @param bool $ignorRequired |
||
0 ignored issues
–
show
There is no parameter named
$ignorRequired . Did you maybe mean $ignoreRequired ?
This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function. It has, however, found a similar but not annotated parameter which might be a good fit. Consider the following example. The parameter /**
* @param array $germany
* @param array $ireland
*/
function finale($germany, $island) {
return "2:1";
}
The most likely cause is that the parameter was changed, but the annotation was not. ![]() |
|||
1303 | * @param string $tablename |
||
1304 | */ |
||
1305 | protected function alterSQLRep($action, array $def, $ignoreRequired, $tablename) |
||
1306 | { |
||
1307 | switch($action){ |
||
1308 | case 'add': |
||
1309 | $f_def=$this->oneColumnSQLRep($def, $ignoreRequired,$tablename,false); |
||
1310 | return "ADD " . $f_def; |
||
1311 | break; |
||
0 ignored issues
–
show
break is not strictly necessary here and could be removed.
The break statement is not necessary if it is preceded for example by a return statement: switch ($x) {
case 1:
return 'foo';
break; // This break is not necessary and can be left off.
}
If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive. ![]() |
|||
1312 | case 'drop': |
||
1313 | return "DROP COLUMN " . $def['name']; |
||
1314 | break; |
||
0 ignored issues
–
show
break is not strictly necessary here and could be removed.
The break statement is not necessary if it is preceded for example by a return statement: switch ($x) {
case 1:
return 'foo';
break; // This break is not necessary and can be left off.
}
If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive. ![]() |
|||
1315 | case 'modify': |
||
1316 | //You cannot specify a default value for a column for MSSQL |
||
1317 | $f_def = $this->oneColumnSQLRep($def, $ignoreRequired,$tablename, true); |
||
1318 | $f_stmt = "ALTER COLUMN ".$f_def['name'].' '.$f_def['colType'].' '. |
||
1319 | $f_def['required'].' '.$f_def['auto_increment']."\n"; |
||
1320 | if (!empty( $f_def['default'])) |
||
1321 | $f_stmt .= " ALTER TABLE " . $tablename . " ADD ". $f_def['default'] . " FOR " . $def['name']; |
||
1322 | return $f_stmt; |
||
1323 | break; |
||
0 ignored issues
–
show
break is not strictly necessary here and could be removed.
The break statement is not necessary if it is preceded for example by a return statement: switch ($x) {
case 1:
return 'foo';
break; // This break is not necessary and can be left off.
}
If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive. ![]() |
|||
1324 | default: |
||
1325 | return ''; |
||
1326 | } |
||
1327 | } |
||
1328 | |||
1329 | /** |
||
1330 | * @see DBManager::changeColumnSQL() |
||
1331 | * |
||
1332 | * MSSQL uses a different syntax than MySQL for table altering that is |
||
1333 | * not quite as simplistic to implement... |
||
1334 | */ |
||
1335 | protected function changeColumnSQL($tablename, $fieldDefs, $action, $ignoreRequired = false) |
||
1336 | { |
||
1337 | $sql=$sql2=''; |
||
1338 | $constraints = $this->get_field_default_constraint_name($tablename); |
||
1339 | $columns = array(); |
||
1340 | if ($this->isFieldArray($fieldDefs)) { |
||
1341 | foreach ($fieldDefs as $def) |
||
1342 | { |
||
1343 | //if the column is being modified drop the default value |
||
1344 | //constraint if it exists. alterSQLRep will add the constraint back |
||
1345 | if (!empty($constraints[$def['name']])) { |
||
1346 | $sql.=" ALTER TABLE " . $tablename . " DROP CONSTRAINT " . $constraints[$def['name']]; |
||
1347 | } |
||
1348 | //check to see if we need to drop related indexes before the alter |
||
1349 | $indices = $this->get_indices($tablename); |
||
1350 | foreach ( $indices as $index ) { |
||
1351 | if ( in_array($def['name'],$index['fields']) ) { |
||
1352 | $sql .= ' ' . $this->add_drop_constraint($tablename,$index,true).' '; |
||
1353 | $sql2 .= ' ' . $this->add_drop_constraint($tablename,$index,false).' '; |
||
1354 | } |
||
1355 | } |
||
1356 | |||
1357 | $columns[] = $this->alterSQLRep($action, $def, $ignoreRequired,$tablename); |
||
1358 | } |
||
1359 | } |
||
1360 | else { |
||
1361 | //if the column is being modified drop the default value |
||
1362 | //constraint if it exists. alterSQLRep will add the constraint back |
||
1363 | if (!empty($constraints[$fieldDefs['name']])) { |
||
1364 | $sql.=" ALTER TABLE " . $tablename . " DROP CONSTRAINT " . $constraints[$fieldDefs['name']]; |
||
1365 | } |
||
1366 | //check to see if we need to drop related indexes before the alter |
||
1367 | $indices = $this->get_indices($tablename); |
||
1368 | foreach ( $indices as $index ) { |
||
1369 | if ( in_array($fieldDefs['name'],$index['fields']) ) { |
||
1370 | $sql .= ' ' . $this->add_drop_constraint($tablename,$index,true).' '; |
||
1371 | $sql2 .= ' ' . $this->add_drop_constraint($tablename,$index,false).' '; |
||
1372 | } |
||
1373 | } |
||
1374 | |||
1375 | |||
1376 | $columns[] = $this->alterSQLRep($action, $fieldDefs, $ignoreRequired,$tablename); |
||
1377 | } |
||
1378 | |||
1379 | $columns = implode(", ", $columns); |
||
1380 | $sql .= " ALTER TABLE $tablename $columns " . $sql2; |
||
1381 | |||
1382 | return $sql; |
||
1383 | } |
||
1384 | |||
1385 | protected function setAutoIncrement($table, $field_name) |
||
1386 | { |
||
1387 | return "identity(1,1)"; |
||
1388 | } |
||
1389 | |||
1390 | /** |
||
1391 | * @see DBManager::setAutoIncrementStart() |
||
1392 | */ |
||
1393 | public function setAutoIncrementStart($table, $field_name, $start_value) |
||
1394 | { |
||
1395 | if($start_value > 1) |
||
1396 | $start_value -= 1; |
||
1397 | $this->query("DBCC CHECKIDENT ('$table', RESEED, $start_value) WITH NO_INFOMSGS"); |
||
1398 | return true; |
||
1399 | } |
||
1400 | |||
1401 | /** |
||
1402 | * @see DBManager::getAutoIncrement() |
||
1403 | */ |
||
1404 | public function getAutoIncrement($table, $field_name) |
||
1405 | { |
||
1406 | $result = $this->getOne("select IDENT_CURRENT('$table') + IDENT_INCR ( '$table' ) as 'Auto_increment'"); |
||
1407 | return $result; |
||
1408 | } |
||
1409 | |||
1410 | /** |
||
1411 | * @see DBManager::get_indices() |
||
1412 | */ |
||
1413 | public function get_indices($tableName) |
||
1414 | { |
||
1415 | //find all unique indexes and primary keys. |
||
1416 | $query = <<<EOSQL |
||
1417 | SELECT sys.tables.object_id, sys.tables.name as table_name, sys.columns.name as column_name, |
||
1418 | sys.indexes.name as index_name, sys.indexes.is_unique, sys.indexes.is_primary_key |
||
1419 | FROM sys.tables, sys.indexes, sys.index_columns, sys.columns |
||
1420 | WHERE (sys.tables.object_id = sys.indexes.object_id |
||
1421 | AND sys.tables.object_id = sys.index_columns.object_id |
||
1422 | AND sys.tables.object_id = sys.columns.object_id |
||
1423 | AND sys.indexes.index_id = sys.index_columns.index_id |
||
1424 | AND sys.index_columns.column_id = sys.columns.column_id) |
||
1425 | AND sys.tables.name = '$tableName' |
||
1426 | EOSQL; |
||
1427 | $result = $this->query($query); |
||
1428 | |||
1429 | $indices = array(); |
||
1430 | while (($row=$this->fetchByAssoc($result)) != null) { |
||
1431 | $index_type = 'index'; |
||
1432 | if ($row['is_primary_key'] == '1') |
||
1433 | $index_type = 'primary'; |
||
1434 | elseif ($row['is_unique'] == 1 ) |
||
1435 | $index_type = 'unique'; |
||
1436 | $name = strtolower($row['index_name']); |
||
1437 | $indices[$name]['name'] = $name; |
||
1438 | $indices[$name]['type'] = $index_type; |
||
1439 | $indices[$name]['fields'][] = strtolower($row['column_name']); |
||
1440 | } |
||
1441 | return $indices; |
||
1442 | } |
||
1443 | |||
1444 | /** |
||
1445 | * @see DBManager::get_columns() |
||
1446 | */ |
||
1447 | public function get_columns($tablename) |
||
1448 | { |
||
1449 | //find all unique indexes and primary keys. |
||
1450 | $result = $this->query("sp_columns $tablename"); |
||
1451 | |||
1452 | $columns = array(); |
||
1453 | while (($row=$this->fetchByAssoc($result)) !=null) { |
||
1454 | $column_name = strtolower($row['COLUMN_NAME']); |
||
1455 | $columns[$column_name]['name']=$column_name; |
||
1456 | $columns[$column_name]['type']=strtolower($row['TYPE_NAME']); |
||
1457 | if ( $row['TYPE_NAME'] == 'decimal' ) { |
||
1458 | $columns[$column_name]['len']=strtolower($row['PRECISION']); |
||
1459 | $columns[$column_name]['len'].=','.strtolower($row['SCALE']); |
||
1460 | } |
||
1461 | elseif ( in_array($row['TYPE_NAME'],array('nchar','nvarchar')) ) |
||
1462 | $columns[$column_name]['len']=strtolower($row['PRECISION']); |
||
1463 | elseif ( !in_array($row['TYPE_NAME'],array('datetime','text')) ) |
||
1464 | $columns[$column_name]['len']=strtolower($row['LENGTH']); |
||
1465 | if ( stristr($row['TYPE_NAME'],'identity') ) { |
||
1466 | $columns[$column_name]['auto_increment'] = '1'; |
||
1467 | $columns[$column_name]['type']=str_replace(' identity','',strtolower($row['TYPE_NAME'])); |
||
1468 | } |
||
1469 | |||
1470 | if (!empty($row['IS_NULLABLE']) && $row['IS_NULLABLE'] == 'NO' && (empty($row['KEY']) || !stristr($row['KEY'],'PRI'))) |
||
1471 | $columns[strtolower($row['COLUMN_NAME'])]['required'] = 'true'; |
||
1472 | |||
1473 | $column_def = 1; |
||
1474 | if ( strtolower($tablename) == 'relationships' ) { |
||
1475 | $column_def = $this->getOne("select cdefault from syscolumns where id = object_id('relationships') and name = '$column_name'"); |
||
1476 | } |
||
1477 | if ( $column_def != 0 && ($row['COLUMN_DEF'] != null)) { // NOTE Not using !empty as an empty string may be a viable default value. |
||
1478 | $matches = array(); |
||
1479 | $row['COLUMN_DEF'] = html_entity_decode($row['COLUMN_DEF'],ENT_QUOTES); |
||
1480 | if ( preg_match('/\([\(|\'](.*)[\)|\']\)/i',$row['COLUMN_DEF'],$matches) ) |
||
1481 | $columns[$column_name]['default'] = $matches[1]; |
||
1482 | elseif ( preg_match('/\(N\'(.*)\'\)/i',$row['COLUMN_DEF'],$matches) ) |
||
1483 | $columns[$column_name]['default'] = $matches[1]; |
||
1484 | else |
||
1485 | $columns[$column_name]['default'] = $row['COLUMN_DEF']; |
||
1486 | } |
||
1487 | } |
||
1488 | return $columns; |
||
1489 | } |
||
1490 | |||
1491 | |||
1492 | /** |
||
1493 | * Get FTS catalog name for current DB |
||
1494 | */ |
||
1495 | protected function ftsCatalogName() |
||
1496 | { |
||
1497 | if(isset($this->connectOptions['db_name'])) { |
||
1498 | return $this->connectOptions['db_name']."_fts_catalog"; |
||
1499 | } |
||
1500 | return 'sugar_fts_catalog'; |
||
1501 | } |
||
1502 | |||
1503 | /** |
||
1504 | * @see DBManager::add_drop_constraint() |
||
1505 | */ |
||
1506 | public function add_drop_constraint($table, $definition, $drop = false) |
||
1507 | { |
||
1508 | $type = $definition['type']; |
||
1509 | $fields = is_array($definition['fields'])?implode(',',$definition['fields']):$definition['fields']; |
||
1510 | $name = $definition['name']; |
||
1511 | $sql = ''; |
||
1512 | |||
1513 | switch ($type){ |
||
1514 | // generic indices |
||
1515 | case 'index': |
||
1516 | case 'alternate_key': |
||
1517 | if ($drop) |
||
1518 | $sql = "DROP INDEX {$name} ON {$table}"; |
||
1519 | else |
||
1520 | $sql = "CREATE INDEX {$name} ON {$table} ({$fields})"; |
||
1521 | break; |
||
1522 | case 'clustered': |
||
1523 | if ($drop) |
||
1524 | $sql = "DROP INDEX {$name} ON {$table}"; |
||
1525 | else |
||
1526 | $sql = "CREATE CLUSTERED INDEX $name ON $table ($fields)"; |
||
1527 | break; |
||
1528 | // constraints as indices |
||
1529 | case 'unique': |
||
1530 | if ($drop) |
||
1531 | $sql = "ALTER TABLE {$table} DROP CONSTRAINT $name"; |
||
1532 | else |
||
1533 | $sql = "ALTER TABLE {$table} ADD CONSTRAINT {$name} UNIQUE ({$fields})"; |
||
1534 | break; |
||
1535 | case 'primary': |
||
1536 | if ($drop) |
||
1537 | $sql = "ALTER TABLE {$table} DROP CONSTRAINT {$name}"; |
||
1538 | else |
||
1539 | $sql = "ALTER TABLE {$table} ADD CONSTRAINT {$name} PRIMARY KEY ({$fields})"; |
||
1540 | break; |
||
1541 | case 'foreign': |
||
1542 | if ($drop) |
||
1543 | $sql = "ALTER TABLE {$table} DROP FOREIGN KEY ({$fields})"; |
||
1544 | else |
||
1545 | $sql = "ALTER TABLE {$table} ADD CONSTRAINT {$name} FOREIGN KEY ({$fields}) REFERENCES {$definition['foreignTable']}({$definition['foreignFields']})"; |
||
1546 | break; |
||
1547 | case 'fulltext': |
||
1548 | if ($this->full_text_indexing_enabled() && $drop) { |
||
1549 | $sql = "DROP FULLTEXT INDEX ON {$table}"; |
||
1550 | } elseif ($this->full_text_indexing_enabled()) { |
||
1551 | $catalog_name=$this->ftsCatalogName(); |
||
1552 | if ( isset($definition['catalog_name']) && $definition['catalog_name'] != 'default') |
||
1553 | $catalog_name = $definition['catalog_name']; |
||
1554 | |||
1555 | $language = "Language 1033"; |
||
1556 | if (isset($definition['language']) && !empty($definition['language'])) |
||
1557 | $language = "Language " . $definition['language']; |
||
1558 | |||
1559 | $key_index = $definition['key_index']; |
||
1560 | |||
1561 | $change_tracking = "auto"; |
||
1562 | if (isset($definition['change_tracking']) && !empty($definition['change_tracking'])) |
||
1563 | $change_tracking = $definition['change_tracking']; |
||
1564 | |||
1565 | $sql = " CREATE FULLTEXT INDEX ON $table ($fields $language) KEY INDEX $key_index ON $catalog_name WITH CHANGE_TRACKING $change_tracking" ; |
||
1566 | } |
||
1567 | break; |
||
1568 | } |
||
1569 | return $sql; |
||
1570 | } |
||
1571 | |||
1572 | /** |
||
1573 | * Returns true if Full Text Search is installed |
||
1574 | * |
||
1575 | * @return bool |
||
1576 | */ |
||
1577 | public function full_text_indexing_installed() |
||
1578 | { |
||
1579 | $ftsChckRes = $this->getOne("SELECT FULLTEXTSERVICEPROPERTY('IsFulltextInstalled') as fts"); |
||
1580 | return !empty($ftsChckRes); |
||
1581 | } |
||
1582 | |||
1583 | /** |
||
1584 | * @see DBManager::full_text_indexing_enabled() |
||
1585 | */ |
||
1586 | protected function full_text_indexing_enabled($dbname = null) |
||
1587 | { |
||
1588 | // check to see if we already have install setting in session |
||
1589 | if(!isset($_SESSION['IsFulltextInstalled'])) |
||
1590 | $_SESSION['IsFulltextInstalled'] = $this->full_text_indexing_installed(); |
||
1591 | |||
1592 | // check to see if FTS Indexing service is installed |
||
1593 | if(empty($_SESSION['IsFulltextInstalled'])) |
||
1594 | return false; |
||
1595 | |||
1596 | // grab the dbname if it was not passed through |
||
1597 | if (empty($dbname)) { |
||
1598 | global $sugar_config; |
||
1599 | $dbname = $sugar_config['dbconfig']['db_name']; |
||
1600 | } |
||
1601 | //we already know that Indexing service is installed, now check |
||
1602 | //to see if it is enabled |
||
1603 | $res = $this->getOne("SELECT DATABASEPROPERTY('$dbname', 'IsFulltextEnabled') ftext"); |
||
1604 | return !empty($res); |
||
1605 | } |
||
1606 | |||
1607 | /** |
||
1608 | * Creates default full text catalog |
||
1609 | */ |
||
1610 | protected function create_default_full_text_catalog() |
||
1611 | { |
||
1612 | if ($this->full_text_indexing_enabled()) { |
||
1613 | $catalog = $this->ftsCatalogName(); |
||
1614 | $GLOBALS['log']->debug("Creating the default catalog for full-text indexing, $catalog"); |
||
1615 | |||
1616 | //drop catalog if exists. |
||
1617 | $ret = $this->query(" |
||
1618 | if not exists( |
||
1619 | select * |
||
1620 | from sys.fulltext_catalogs |
||
1621 | where name ='$catalog' |
||
1622 | ) |
||
1623 | CREATE FULLTEXT CATALOG $catalog"); |
||
1624 | |||
1625 | if (empty($ret)) { |
||
1626 | $GLOBALS['log']->error("Error creating default full-text catalog, $catalog"); |
||
1627 | } |
||
1628 | } |
||
1629 | } |
||
1630 | |||
1631 | /** |
||
1632 | * Function returns name of the constraint automatically generated by sql-server. |
||
1633 | * We request this for default, primary key, required |
||
1634 | * |
||
1635 | * @param string $table |
||
1636 | * @param string $column |
||
1637 | * @return string |
||
1638 | */ |
||
1639 | private function get_field_default_constraint_name($table, $column = null) |
||
1640 | { |
||
1641 | static $results = array(); |
||
1642 | |||
1643 | if ( empty($column) && isset($results[$table]) ) |
||
1644 | return $results[$table]; |
||
1645 | |||
1646 | $query = <<<EOQ |
||
1647 | select s.name, o.name, c.name dtrt, d.name ctrt |
||
1648 | from sys.default_constraints as d |
||
1649 | join sys.objects as o |
||
1650 | on o.object_id = d.parent_object_id |
||
1651 | join sys.columns as c |
||
1652 | on c.object_id = o.object_id and c.column_id = d.parent_column_id |
||
1653 | join sys.schemas as s |
||
1654 | on s.schema_id = o.schema_id |
||
1655 | where o.name = '$table' |
||
1656 | EOQ; |
||
1657 | if ( !empty($column) ) |
||
1658 | $query .= " and c.name = '$column'"; |
||
1659 | $res = $this->query($query); |
||
1660 | if ( !empty($column) ) { |
||
1661 | $row = $this->fetchByAssoc($res); |
||
1662 | if (!empty($row)) |
||
1663 | return $row['ctrt']; |
||
1664 | } |
||
1665 | else { |
||
1666 | $returnResult = array(); |
||
1667 | while ( $row = $this->fetchByAssoc($res) ) |
||
1668 | $returnResult[$row['dtrt']] = $row['ctrt']; |
||
1669 | $results[$table] = $returnResult; |
||
1670 | return $returnResult; |
||
1671 | } |
||
1672 | |||
1673 | return null; |
||
1674 | } |
||
1675 | |||
1676 | /** |
||
1677 | * @see DBManager::massageFieldDef() |
||
1678 | */ |
||
1679 | public function massageFieldDef(&$fieldDef, $tablename) |
||
1680 | { |
||
1681 | parent::massageFieldDef($fieldDef,$tablename); |
||
1682 | |||
1683 | if ($fieldDef['type'] == 'int') |
||
1684 | $fieldDef['len'] = '4'; |
||
1685 | |||
1686 | if(empty($fieldDef['len'])) |
||
1687 | { |
||
1688 | switch($fieldDef['type']) { |
||
1689 | case 'bit' : |
||
0 ignored issues
–
show
There must be no space before the colon in a CASE statement
As per the PSR-2 coding standard, there must not be a space in front of the colon in case statements. switch ($selector) {
case "A": //right
doSomething();
break;
case "B" : //wrong
doSomethingElse();
break;
}
To learn more about the PSR-2 coding standard, please refer to the PHP-Fig. ![]() |
|||
1690 | case 'bool' : $fieldDef['len'] = '1'; break; |
||
0 ignored issues
–
show
There must be no space before the colon in a CASE statement
As per the PSR-2 coding standard, there must not be a space in front of the colon in case statements. switch ($selector) {
case "A": //right
doSomething();
break;
case "B" : //wrong
doSomethingElse();
break;
}
To learn more about the PSR-2 coding standard, please refer to the PHP-Fig. ![]() The case body in a switch statement must start on the line following the statement.
According to the PSR-2, the body of a case statement must start on the line immediately following the case statement. switch ($expr) {
case "A":
doSomething(); //right
break;
case "B":
doSomethingElse(); //wrong
break;
} To learn more about the PSR-2 coding standard, please refer to the PHP-Fig. ![]() Terminating statement must be on a line by itself
As per the PSR-2 coding standard, the switch ($expr) {
case "A":
doSomething();
break; //wrong
case "B":
doSomething();
break; //right
case "C:":
doSomething();
return true; //right
}
To learn more about the PSR-2 coding standard, please refer to the PHP-Fig. ![]() |
|||
1691 | case 'smallint' : $fieldDef['len'] = '2'; break; |
||
0 ignored issues
–
show
There must be no space before the colon in a CASE statement
As per the PSR-2 coding standard, there must not be a space in front of the colon in case statements. switch ($selector) {
case "A": //right
doSomething();
break;
case "B" : //wrong
doSomethingElse();
break;
}
To learn more about the PSR-2 coding standard, please refer to the PHP-Fig. ![]() The case body in a switch statement must start on the line following the statement.
According to the PSR-2, the body of a case statement must start on the line immediately following the case statement. switch ($expr) {
case "A":
doSomething(); //right
break;
case "B":
doSomethingElse(); //wrong
break;
} To learn more about the PSR-2 coding standard, please refer to the PHP-Fig. ![]() Terminating statement must be on a line by itself
As per the PSR-2 coding standard, the switch ($expr) {
case "A":
doSomething();
break; //wrong
case "B":
doSomething();
break; //right
case "C:":
doSomething();
return true; //right
}
To learn more about the PSR-2 coding standard, please refer to the PHP-Fig. ![]() |
|||
1692 | case 'float' : $fieldDef['len'] = '8'; break; |
||
0 ignored issues
–
show
There must be no space before the colon in a CASE statement
As per the PSR-2 coding standard, there must not be a space in front of the colon in case statements. switch ($selector) {
case "A": //right
doSomething();
break;
case "B" : //wrong
doSomethingElse();
break;
}
To learn more about the PSR-2 coding standard, please refer to the PHP-Fig. ![]() The case body in a switch statement must start on the line following the statement.
According to the PSR-2, the body of a case statement must start on the line immediately following the case statement. switch ($expr) {
case "A":
doSomething(); //right
break;
case "B":
doSomethingElse(); //wrong
break;
} To learn more about the PSR-2 coding standard, please refer to the PHP-Fig. ![]() Terminating statement must be on a line by itself
As per the PSR-2 coding standard, the switch ($expr) {
case "A":
doSomething();
break; //wrong
case "B":
doSomething();
break; //right
case "C:":
doSomething();
return true; //right
}
To learn more about the PSR-2 coding standard, please refer to the PHP-Fig. ![]() |
|||
1693 | case 'varchar' : |
||
0 ignored issues
–
show
There must be no space before the colon in a CASE statement
As per the PSR-2 coding standard, there must not be a space in front of the colon in case statements. switch ($selector) {
case "A": //right
doSomething();
break;
case "B" : //wrong
doSomethingElse();
break;
}
To learn more about the PSR-2 coding standard, please refer to the PHP-Fig. ![]() |
|||
1694 | case 'nvarchar' : |
||
0 ignored issues
–
show
There must be no space before the colon in a CASE statement
As per the PSR-2 coding standard, there must not be a space in front of the colon in case statements. switch ($selector) {
case "A": //right
doSomething();
break;
case "B" : //wrong
doSomethingElse();
break;
}
To learn more about the PSR-2 coding standard, please refer to the PHP-Fig. ![]() |
|||
1695 | $fieldDef['len'] = $this->isTextType($fieldDef['dbType']) ? 'max' : '255'; |
||
1696 | break; |
||
1697 | case 'image' : $fieldDef['len'] = '2147483647'; break; |
||
0 ignored issues
–
show
There must be no space before the colon in a CASE statement
As per the PSR-2 coding standard, there must not be a space in front of the colon in case statements. switch ($selector) {
case "A": //right
doSomething();
break;
case "B" : //wrong
doSomethingElse();
break;
}
To learn more about the PSR-2 coding standard, please refer to the PHP-Fig. ![]() The case body in a switch statement must start on the line following the statement.
According to the PSR-2, the body of a case statement must start on the line immediately following the case statement. switch ($expr) {
case "A":
doSomething(); //right
break;
case "B":
doSomethingElse(); //wrong
break;
} To learn more about the PSR-2 coding standard, please refer to the PHP-Fig. ![]() Terminating statement must be on a line by itself
As per the PSR-2 coding standard, the switch ($expr) {
case "A":
doSomething();
break; //wrong
case "B":
doSomething();
break; //right
case "C:":
doSomething();
return true; //right
}
To learn more about the PSR-2 coding standard, please refer to the PHP-Fig. ![]() |
|||
1698 | case 'ntext' : $fieldDef['len'] = '2147483646'; break; // Note: this is from legacy code, don't know if this is correct |
||
0 ignored issues
–
show
There must be no space before the colon in a CASE statement
As per the PSR-2 coding standard, there must not be a space in front of the colon in case statements. switch ($selector) {
case "A": //right
doSomething();
break;
case "B" : //wrong
doSomethingElse();
break;
}
To learn more about the PSR-2 coding standard, please refer to the PHP-Fig. ![]() The case body in a switch statement must start on the line following the statement.
According to the PSR-2, the body of a case statement must start on the line immediately following the case statement. switch ($expr) {
case "A":
doSomething(); //right
break;
case "B":
doSomethingElse(); //wrong
break;
} To learn more about the PSR-2 coding standard, please refer to the PHP-Fig. ![]() Terminating statement must be on a line by itself
As per the PSR-2 coding standard, the switch ($expr) {
case "A":
doSomething();
break; //wrong
case "B":
doSomething();
break; //right
case "C:":
doSomething();
return true; //right
}
To learn more about the PSR-2 coding standard, please refer to the PHP-Fig. ![]() |
|||
1699 | } |
||
1700 | } |
||
1701 | if($fieldDef['type'] == 'decimal' |
||
1702 | && empty($fieldDef['precision']) |
||
1703 | && !strpos($fieldDef['len'], ',')) |
||
1704 | { |
||
1705 | $fieldDef['len'] .= ',0'; // Adding 0 precision if it is not specified |
||
1706 | } |
||
1707 | |||
1708 | if(empty($fieldDef['default']) |
||
1709 | && in_array($fieldDef['type'],array('bit','bool'))) |
||
1710 | { |
||
1711 | $fieldDef['default'] = '0'; |
||
1712 | } |
||
1713 | if (isset($fieldDef['required']) && $fieldDef['required'] && !isset($fieldDef['default']) ) |
||
1714 | $fieldDef['default'] = ''; |
||
1715 | // if ($fieldDef['type'] == 'bit' && empty($fieldDef['len']) ) |
||
1716 | // $fieldDef['len'] = '1'; |
||
1717 | // if ($fieldDef['type'] == 'bool' && empty($fieldDef['len']) ) |
||
1718 | // $fieldDef['len'] = '1'; |
||
1719 | // if ($fieldDef['type'] == 'float' && empty($fieldDef['len']) ) |
||
1720 | // $fieldDef['len'] = '8'; |
||
1721 | // if ($fieldDef['type'] == 'varchar' && empty($fieldDef['len']) ) |
||
1722 | // $fieldDef['len'] = '255'; |
||
1723 | // if ($fieldDef['type'] == 'nvarchar' && empty($fieldDef['len']) ) |
||
1724 | // $fieldDef['len'] = '255'; |
||
1725 | // if ($fieldDef['type'] == 'image' && empty($fieldDef['len']) ) |
||
1726 | // $fieldDef['len'] = '2147483647'; |
||
1727 | // if ($fieldDef['type'] == 'ntext' && empty($fieldDef['len']) ) |
||
1728 | // $fieldDef['len'] = '2147483646'; |
||
1729 | // if ($fieldDef['type'] == 'smallint' && empty($fieldDef['len']) ) |
||
1730 | // $fieldDef['len'] = '2'; |
||
1731 | // if ($fieldDef['type'] == 'bit' && empty($fieldDef['default']) ) |
||
1732 | // $fieldDef['default'] = '0'; |
||
1733 | // if ($fieldDef['type'] == 'bool' && empty($fieldDef['default']) ) |
||
1734 | // $fieldDef['default'] = '0'; |
||
1735 | |||
1736 | } |
||
1737 | |||
1738 | /** |
||
1739 | * @see DBManager::oneColumnSQLRep() |
||
1740 | */ |
||
1741 | protected function oneColumnSQLRep($fieldDef, $ignoreRequired = false, $table = '', $return_as_array = false) |
||
1742 | { |
||
1743 | //Bug 25814 |
||
1744 | if(isset($fieldDef['name'])){ |
||
1745 | $colType = $this->getFieldType($fieldDef); |
||
1746 | if(stristr($this->getFieldType($fieldDef), 'decimal') && isset($fieldDef['len'])){ |
||
1747 | $fieldDef['len'] = min($fieldDef['len'],38); |
||
1748 | } |
||
1749 | //bug: 39690 float(8) is interpreted as real and this generates a diff when doing repair |
||
1750 | if(stristr($colType, 'float') && isset($fieldDef['len']) && $fieldDef['len'] == 8){ |
||
1751 | unset($fieldDef['len']); |
||
1752 | } |
||
1753 | } |
||
1754 | |||
1755 | // always return as array for post-processing |
||
1756 | $ref = parent::oneColumnSQLRep($fieldDef, $ignoreRequired, $table, true); |
||
1757 | |||
1758 | // Bug 24307 - Don't add precision for float fields. |
||
1759 | if ( stristr($ref['colType'],'float') ) |
||
1760 | $ref['colType'] = preg_replace('/(,\d+)/','',$ref['colType']); |
||
1761 | |||
1762 | if ( $return_as_array ) |
||
1763 | return $ref; |
||
1764 | else |
||
1765 | return "{$ref['name']} {$ref['colType']} {$ref['default']} {$ref['required']} {$ref['auto_increment']}"; |
||
1766 | } |
||
1767 | |||
1768 | /** |
||
1769 | * Saves changes to module's audit table |
||
1770 | * |
||
1771 | * @param object $bean Sugarbean instance |
||
1772 | * @param array $changes changes |
||
1773 | */ |
||
1774 | public function save_audit_records(SugarBean $bean, $changes) |
||
1775 | { |
||
1776 | //Bug 25078 fixed by Martin Hu: sqlserver haven't 'date' type, trim extra "00:00:00" |
||
1777 | if($changes['data_type'] == 'date'){ |
||
1778 | $changes['before'] = str_replace(' 00:00:00','',$changes['before']); |
||
1779 | } |
||
1780 | parent::save_audit_records($bean,$changes); |
||
1781 | } |
||
1782 | |||
1783 | /** |
||
1784 | * Disconnects from the database |
||
1785 | * |
||
1786 | * Also handles any cleanup needed |
||
1787 | */ |
||
1788 | public function disconnect() |
||
1789 | { |
||
1790 | $GLOBALS['log']->debug('Calling Mssql::disconnect()'); |
||
1791 | if(!empty($this->database)){ |
||
1792 | $this->freeResult(); |
||
1793 | mssql_close($this->database); |
||
1794 | $this->database = null; |
||
1795 | } |
||
1796 | } |
||
1797 | |||
1798 | /** |
||
1799 | * @see DBManager::freeDbResult() |
||
1800 | */ |
||
1801 | protected function freeDbResult($dbResult) |
||
1802 | { |
||
1803 | if(!empty($dbResult)) |
||
1804 | mssql_free_result($dbResult); |
||
1805 | } |
||
1806 | |||
1807 | /** |
||
1808 | * (non-PHPdoc) |
||
1809 | * @see DBManager::lastDbError() |
||
1810 | */ |
||
1811 | public function lastDbError() |
||
1812 | { |
||
1813 | $sqlmsg = mssql_get_last_message(); |
||
1814 | if(empty($sqlmsg)) return false; |
||
1815 | global $app_strings; |
||
1816 | if (empty($app_strings) |
||
1817 | or !isset($app_strings['ERR_MSSQL_DB_CONTEXT']) |
||
1818 | or !isset($app_strings['ERR_MSSQL_WARNING']) ) { |
||
1819 | //ignore the message from sql-server if $app_strings array is empty. This will happen |
||
1820 | //only if connection if made before language is set. |
||
1821 | return false; |
||
1822 | } |
||
1823 | |||
1824 | $sqlpos = strpos($sqlmsg, 'Changed database context to'); |
||
1825 | $sqlpos2 = strpos($sqlmsg, 'Warning:'); |
||
1826 | $sqlpos3 = strpos($sqlmsg, 'Checking identity information:'); |
||
1827 | if ( $sqlpos !== false || $sqlpos2 !== false || $sqlpos3 !== false ) { |
||
1828 | return false; |
||
1829 | } else { |
||
1830 | global $app_strings; |
||
1831 | //ERR_MSSQL_DB_CONTEXT: localized version of 'Changed database context to' message |
||
1832 | if (empty($app_strings) or !isset($app_strings['ERR_MSSQL_DB_CONTEXT'])) { |
||
1833 | //ignore the message from sql-server if $app_strings array is empty. This will happen |
||
1834 | //only if connection if made before languge is set. |
||
1835 | $GLOBALS['log']->debug("Ignoring this database message: " . $sqlmsg); |
||
1836 | return false; |
||
1837 | } |
||
1838 | else { |
||
1839 | $sqlpos = strpos($sqlmsg, $app_strings['ERR_MSSQL_DB_CONTEXT']); |
||
1840 | if ( $sqlpos !== false ) |
||
1841 | return false; |
||
1842 | } |
||
1843 | } |
||
1844 | |||
1845 | if ( strlen($sqlmsg) > 2 ) { |
||
1846 | return "SQL Server error: " . $sqlmsg; |
||
1847 | } |
||
1848 | |||
1849 | return false; |
||
1850 | } |
||
1851 | |||
1852 | /** |
||
1853 | * (non-PHPdoc) |
||
1854 | * @see DBManager::getDbInfo() |
||
1855 | */ |
||
1856 | public function getDbInfo() |
||
1857 | { |
||
1858 | return array("version" => $this->version()); |
||
1859 | } |
||
1860 | |||
1861 | /** |
||
1862 | * (non-PHPdoc) |
||
1863 | * @see DBManager::validateQuery() |
||
1864 | */ |
||
1865 | public function validateQuery($query) |
||
1866 | { |
||
1867 | if(!$this->isSelect($query)) { |
||
1868 | return false; |
||
1869 | } |
||
1870 | $this->query("SET SHOWPLAN_TEXT ON"); |
||
1871 | $res = $this->getOne($query); |
||
1872 | $this->query("SET SHOWPLAN_TEXT OFF"); |
||
1873 | return !empty($res); |
||
1874 | } |
||
1875 | |||
1876 | /** |
||
1877 | * This is a utility function to prepend the "N" character in front of SQL values that are |
||
1878 | * surrounded by single quotes. |
||
1879 | * |
||
1880 | * @param $sql string SQL statement |
||
1881 | * @return string SQL statement with single quote values prepended with "N" character for nvarchar columns |
||
1882 | */ |
||
1883 | protected function _appendN($sql) |
||
1884 | { |
||
1885 | // If there are no single quotes, don't bother, will just assume there is no character data |
||
1886 | if (strpos($sql, "'") === false) |
||
1887 | return $sql; |
||
1888 | |||
1889 | // Flag if there are odd number of single quotes, just continue without trying to append N |
||
1890 | if ((substr_count($sql, "'") & 1)) { |
||
1891 | $GLOBALS['log']->error("SQL statement[" . $sql . "] has odd number of single quotes."); |
||
1892 | return $sql; |
||
1893 | } |
||
1894 | |||
1895 | //The only location of three subsequent ' will be at the beginning or end of a value. |
||
1896 | $sql = preg_replace('/(?<!\')(\'{3})(?!\')/', "'<@#@#@PAIR@#@#@>", $sql); |
||
1897 | |||
1898 | // Remove any remaining '' and do not parse... replace later (hopefully we don't even have any) |
||
1899 | $pairs = array(); |
||
1900 | $regexp = '/(\'{2})/'; |
||
1901 | $pair_matches = array(); |
||
1902 | preg_match_all($regexp, $sql, $pair_matches); |
||
1903 | if ($pair_matches) { |
||
1904 | foreach (array_unique($pair_matches[0]) as $key=>$value) { |
||
1905 | $pairs['<@PAIR-'.$key.'@>'] = $value; |
||
1906 | } |
||
1907 | if (!empty($pairs)) { |
||
1908 | $sql = str_replace($pairs, array_keys($pairs), $sql); |
||
1909 | } |
||
1910 | } |
||
1911 | |||
1912 | $regexp = "/(N?'.+?')/is"; |
||
1913 | $matches = array(); |
||
1914 | preg_match_all($regexp, $sql, $matches); |
||
1915 | $replace = array(); |
||
1916 | if (!empty($matches)) { |
||
1917 | foreach ($matches[0] as $value) { |
||
1918 | // We are assuming that all nvarchar columns are no more than 200 characters in length |
||
1919 | // One problem we face is the image column type in reports which cannot accept nvarchar data |
||
1920 | if (!empty($value) && !is_numeric(trim(str_replace(array("'", ","), "", $value))) && !preg_match('/^\'[\,]\'$/', $value)) { |
||
1921 | $replace[$value] = 'N' . trim($value, "N"); |
||
1922 | } |
||
1923 | } |
||
1924 | } |
||
1925 | |||
1926 | if (!empty($replace)) |
||
1927 | $sql = str_replace(array_keys($replace), $replace, $sql); |
||
1928 | |||
1929 | if (!empty($pairs)) |
||
1930 | $sql = str_replace(array_keys($pairs), $pairs, $sql); |
||
1931 | |||
1932 | if(strpos($sql, "<@#@#@PAIR@#@#@>")) |
||
1933 | $sql = str_replace(array('<@#@#@PAIR@#@#@>'), array("''"), $sql); |
||
1934 | |||
1935 | return $sql; |
||
1936 | } |
||
1937 | |||
1938 | /** |
||
1939 | * Quote SQL Server search term |
||
1940 | * @param string $term |
||
1941 | * @return string |
||
1942 | */ |
||
1943 | protected function quoteTerm($term) |
||
1944 | { |
||
1945 | $term = str_replace("%", "*", $term); // Mssql wildcard is * |
||
1946 | return '"'.str_replace('"', '', $term).'"'; |
||
1947 | } |
||
1948 | |||
1949 | /** |
||
1950 | * Generate fulltext query from set of terms |
||
1951 | * @param string $fields Field to search against |
||
0 ignored issues
–
show
There is no parameter named
$fields . Did you maybe mean $field ?
This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function. It has, however, found a similar but not annotated parameter which might be a good fit. Consider the following example. The parameter /**
* @param array $germany
* @param array $ireland
*/
function finale($germany, $island) {
return "2:1";
}
The most likely cause is that the parameter was changed, but the annotation was not. ![]() |
|||
1952 | * @param array $terms Search terms that may be or not be in the result |
||
1953 | * @param array $must_terms Search terms that have to be in the result |
||
1954 | * @param array $exclude_terms Search terms that have to be not in the result |
||
1955 | */ |
||
1956 | public function getFulltextQuery($field, $terms, $must_terms = array(), $exclude_terms = array()) |
||
1957 | { |
||
1958 | $condition = $or_condition = array(); |
||
1959 | foreach($must_terms as $term) { |
||
1960 | $condition[] = $this->quoteTerm($term); |
||
1961 | } |
||
1962 | |||
1963 | foreach($terms as $term) { |
||
1964 | $or_condition[] = $this->quoteTerm($term); |
||
1965 | } |
||
1966 | |||
1967 | if(!empty($or_condition)) { |
||
1968 | $condition[] = "(".join(" | ", $or_condition).")"; |
||
1969 | } |
||
1970 | |||
1971 | foreach($exclude_terms as $term) { |
||
1972 | $condition[] = " NOT ".$this->quoteTerm($term); |
||
1973 | } |
||
1974 | $condition = $this->quoted(join(" AND ",$condition)); |
||
1975 | return "CONTAINS($field, $condition)"; |
||
1976 | } |
||
1977 | |||
1978 | /** |
||
1979 | * Check if certain database exists |
||
1980 | * @param string $dbname |
||
1981 | */ |
||
1982 | public function dbExists($dbname) |
||
1983 | { |
||
1984 | $db = $this->getOne("SELECT name FROM master..sysdatabases WHERE name = N".$this->quoted($dbname)); |
||
1985 | return !empty($db); |
||
1986 | } |
||
1987 | |||
1988 | /** |
||
1989 | * Select database |
||
1990 | * @param string $dbname |
||
1991 | */ |
||
1992 | protected function selectDb($dbname) |
||
1993 | { |
||
1994 | return mssql_select_db($dbname); |
||
1995 | } |
||
1996 | |||
1997 | /** |
||
1998 | * Check if certain DB user exists |
||
1999 | * @param string $username |
||
2000 | */ |
||
2001 | public function userExists($username) |
||
2002 | { |
||
2003 | $this->selectDb("master"); |
||
2004 | $user = $this->getOne("select count(*) from sys.sql_logins where name =".$this->quoted($username)); |
||
2005 | // FIXME: go back to the original DB |
||
2006 | return !empty($user); |
||
2007 | } |
||
2008 | |||
2009 | /** |
||
2010 | * Create DB user |
||
2011 | * @param string $database_name |
||
2012 | * @param string $host_name |
||
2013 | * @param string $user |
||
2014 | * @param string $password |
||
2015 | */ |
||
2016 | public function createDbUser($database_name, $host_name, $user, $password) |
||
2017 | { |
||
2018 | $qpassword = $this->quote($password); |
||
2019 | $this->selectDb($database_name); |
||
2020 | $this->query("CREATE LOGIN $user WITH PASSWORD = '$qpassword'", true); |
||
2021 | $this->query("CREATE USER $user FOR LOGIN $user", true); |
||
2022 | $this->query("EXEC sp_addRoleMember 'db_ddladmin ', '$user'", true); |
||
2023 | $this->query("EXEC sp_addRoleMember 'db_datareader','$user'", true); |
||
2024 | $this->query("EXEC sp_addRoleMember 'db_datawriter','$user'", true); |
||
2025 | } |
||
2026 | |||
2027 | /** |
||
2028 | * Create a database |
||
2029 | * @param string $dbname |
||
2030 | */ |
||
2031 | public function createDatabase($dbname) |
||
2032 | { |
||
2033 | return $this->query("CREATE DATABASE $dbname", true); |
||
2034 | } |
||
2035 | |||
2036 | /** |
||
2037 | * Drop a database |
||
2038 | * @param string $dbname |
||
2039 | */ |
||
2040 | public function dropDatabase($dbname) |
||
2041 | { |
||
2042 | return $this->query("DROP DATABASE $dbname", true); |
||
2043 | } |
||
2044 | |||
2045 | /** |
||
2046 | * Check if this driver can be used |
||
2047 | * @return bool |
||
2048 | */ |
||
2049 | public function valid() |
||
2050 | { |
||
2051 | return function_exists("mssql_connect"); |
||
2052 | } |
||
2053 | |||
2054 | /** |
||
2055 | * Check if this DB name is valid |
||
2056 | * |
||
2057 | * @param string $name |
||
2058 | * @return bool |
||
2059 | */ |
||
2060 | public function isDatabaseNameValid($name) |
||
2061 | { |
||
2062 | // No funny chars, does not begin with number |
||
2063 | return preg_match('/^[0-9#@]+|[\"\'\*\/\\?\:\\<\>\-\ \&\!\(\)\[\]\{\}\;\,\.\`\~\|\\\\]+/', $name)==0; |
||
2064 | } |
||
2065 | |||
2066 | public function installConfig() |
||
2067 | { |
||
2068 | return array( |
||
2069 | 'LBL_DBCONFIG_MSG3' => array( |
||
2070 | "setup_db_database_name" => array("label" => 'LBL_DBCONF_DB_NAME', "required" => true), |
||
2071 | ), |
||
2072 | 'LBL_DBCONFIG_MSG2' => array( |
||
2073 | "setup_db_host_name" => array("label" => 'LBL_DBCONF_HOST_NAME', "required" => true), |
||
2074 | "setup_db_host_instance" => array("label" => 'LBL_DBCONF_HOST_INSTANCE'), |
||
2075 | ), |
||
2076 | 'LBL_DBCONF_TITLE_USER_INFO' => array(), |
||
2077 | 'LBL_DBCONFIG_B_MSG1' => array( |
||
2078 | "setup_db_admin_user_name" => array("label" => 'LBL_DBCONF_DB_ADMIN_USER', "required" => true), |
||
2079 | "setup_db_admin_password" => array("label" => 'LBL_DBCONF_DB_ADMIN_PASSWORD', "type" => "password"), |
||
2080 | ) |
||
2081 | ); |
||
2082 | } |
||
2083 | |||
2084 | /** |
||
2085 | * Returns a DB specific FROM clause which can be used to select against functions. |
||
2086 | * Note that depending on the database that this may also be an empty string. |
||
2087 | * @return string |
||
2088 | */ |
||
2089 | public function getFromDummyTable() |
||
2090 | { |
||
2091 | return ''; |
||
2092 | } |
||
2093 | |||
2094 | /** |
||
2095 | * Returns a DB specific piece of SQL which will generate GUID (UUID) |
||
2096 | * This string can be used in dynamic SQL to do multiple inserts with a single query. |
||
2097 | * I.e. generate a unique Sugar id in a sub select of an insert statement. |
||
2098 | * @return string |
||
2099 | */ |
||
2100 | |||
2101 | public function getGuidSQL() |
||
2102 | { |
||
2103 | return 'NEWID()'; |
||
2104 | } |
||
2105 | } |
||
2106 |
This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.
If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.
In this case you can add the
@ignore
PhpDoc annotation to the duplicate definition and it will be ignored.