Tripal v1.0 (6.x-1.0)
|
00001 <?php 00002 00003 require_once "tripal_core.schema_v1.2.api.inc"; 00004 require_once "tripal_core.schema_v1.11.api.inc"; 00005 00105 function tripal_core_chado_insert($table, $values, $options = array()) { 00106 00107 if (!is_array($values)) { 00108 watchdog('tripal_core', 'Cannot pass non array as values for inserting.', array(), 00109 WATCHDOG_ERROR); 00110 return FALSE; 00111 } 00112 if (count($values)==0) { 00113 watchdog('tripal_core', 'Cannot pass an empty array as values for inserting.', array(), 00114 WATCHDOG_ERROR); 00115 return FALSE; 00116 } 00117 00118 // set defaults for options. If we don't set defaults then 00119 // we get memory leaks when we try to access the elements 00120 if (!is_array($options)) { 00121 $options = array(); 00122 } 00123 if (!array_key_exists('is_prepared', $options)) { 00124 $options['is_prepared'] = FALSE; 00125 } 00126 if (!array_key_exists('statement_name', $options)) { 00127 $options['statement_name'] = FALSE; 00128 } 00129 if (!array_key_exists('skip_validation', $options)) { 00130 $options['skip_validation'] = FALSE; 00131 } 00132 if (!array_key_exists('return_record', $options)) { 00133 $options['return_record'] = TRUE; 00134 } 00135 00136 $insert_values = array(); 00137 00138 // Determine plan of action 00139 if ($options['statement_name']) { 00140 // we have a prepared statment (or want to create one) so set $prepared = TRUE 00141 $prepared = TRUE; 00142 00143 // we need to get a persistent connection. If one exists this function 00144 // will not recreate it, but if not it will create one and store it in 00145 // a Drupal variable for reuse later. 00146 $connection = tripal_db_persistent_chado(); 00147 00148 // if we cannot get a connection the abandon the prepared statement 00149 if (!$connection) { 00150 $prepared = FALSE; 00151 unset($options['statement_name']); 00152 } 00153 } 00154 else { 00155 //print "NO STATEMENT (insert): $table\n"; 00156 //debug_print_backtrace(); 00157 } 00158 00159 if (array_key_exists('skip_validation', $options)) { 00160 $validate = !$options['skip_validation']; 00161 } 00162 else { 00163 $validate = TRUE; 00164 } 00165 00166 // get the table description 00167 $table_desc = tripal_core_get_chado_table_schema($table); 00168 if (empty($table_desc)) { 00169 watchdog('tripal_core', 'tripal_core_chado_insert: There is no table description for !table_name', array('!table_name' => $table), WATCHDOG_WARNING); 00170 } 00171 00172 // iterate through the values array and create a new 'insert_values' array 00173 // that has all the values needed for insert with all foreign relationsihps 00174 // resolved. 00175 foreach ($values as $field => $value) { 00176 // make sure the field is in the table description. If not then return an error 00177 // message 00178 if (!array_key_exists($field, $table_desc['fields'])) { 00179 watchdog('tripal_core', "tripal_core_chado_insert: The field '%field' does not exist ". 00180 "for the table '%table'. Cannot perform insert. Values: %array", 00181 array('%field' => $field, '%table' => $table, '%array' => print_r($values, 1)), WATCHDOG_ERROR); 00182 return FALSE; 00183 } 00184 00185 if (is_array($value)) { 00186 $foreign_options = array(); 00187 if ($options['statement_name']) { 00188 // add the fk relationship info to the prepared statement name so that 00189 // we can prepare the selects run by the recrusive tripal_core_chado_get_foreign_key 00190 // function. 00191 $fk_sname = "fk_" . $table . "_" . $field; 00192 foreach ($value as $k => $v) { 00193 $fk_sname .= substr($k, 0, 2); 00194 } 00195 $foreign_options['statement_name'] = $fk_sname; 00196 } 00197 // select the value from the foreign key relationship for this value 00198 $results = tripal_core_chado_get_foreign_key($table_desc, $field, $value, $foreign_options); 00199 00200 if (sizeof($results) > 1) { 00201 watchdog('tripal_core', 'tripal_core_chado_insert: Too many records match the criteria supplied for !foreign_key foreign key constraint (!criteria)', array('!foreign_key' => $field, '!criteria' => print_r($value, TRUE)), WATCHDOG_ERROR); 00202 } 00203 elseif (sizeof($results) < 1) { 00204 //watchdog('tripal_core', 'tripal_core_chado_insert: no record matches criteria supplied for !foreign_key foreign key constraint (!criteria)', array('!foreign_key' => $field, '!criteria' => print_r($value, TRUE)), WATCHDOG_ERROR); 00205 } 00206 else { 00207 $insert_values[$field] = $results[0]; 00208 } 00209 } 00210 else { 00211 $insert_values[$field] = $value; 00212 } 00213 } 00214 00215 if ($validate) { 00216 00217 // check for violation of any unique constraints 00218 $ukeys = array(); 00219 if (array_key_exists('unique keys', $table_desc)) { 00220 $ukeys = $table_desc['unique keys']; 00221 } 00222 $ukselect_cols = array(); 00223 $ukselect_vals = array(); 00224 if ($ukeys) { 00225 foreach ($ukeys as $name => $fields) { 00226 foreach ($fields as $index => $field) { 00227 // build the arrays for performing a select that will check the contraint 00228 $ukselect_cols[] = $field; 00229 if (!array_key_exists($field, $insert_values)) { 00230 if (array_key_exists('default', $table_desc['fields'][$field])) { 00231 $ukselect_vals[$field] = $table_desc['fields'][$field]['default']; 00232 } 00233 } 00234 else { 00235 $ukselect_vals[$field] = $insert_values[$field]; 00236 } 00237 } 00238 // now check the constraint 00239 $coptions = array(); 00240 if ($options['statement_name']) { 00241 $coptions = array('statement_name' => 'uqsel_' . $table . '_' . $name); 00242 } 00243 if (tripal_core_chado_select($table, $ukselect_cols, $ukselect_vals, $coptions)) { 00244 watchdog('tripal_core', "tripal_core_chado_insert: Cannot insert duplicate record into $table table: " . 00245 print_r($values, 1), array(), 'WATCHDOG_ERROR'); 00246 return FALSE; 00247 } 00248 } 00249 } 00250 00251 // if trying to insert a field that is the primary key, make sure it also is unique 00252 if (array_key_exists('primary key', $table_desc)) { 00253 $pkey = $table_desc['primary key'][0]; 00254 if (array_key_exists($pkey, $insert_values)) { 00255 $coptions = array('statement_name' => 'pqsel_' . $table . '_' . $pkey); 00256 if (tripal_core_chado_select($table, array($pkey), array($pkey => $insert_values[$pkey]), $coptions)) { 00257 watchdog('tripal_core', "tripal_core_chado_insert: Cannot insert duplicate primary key into $table table: " . print_r($values, 1), array(), 'WATCHDOG_ERROR'); 00258 return FALSE; 00259 } 00260 } 00261 } 00262 00263 // make sure required fields have a value 00264 if (!is_array($table_desc['fields'])) { 00265 $table_desc['fields'] = array(); 00266 watchdog('tripal_core', "tripal_core_chado_insert: %table missing fields: \n %schema", 00267 array('%table' => $table, '%schema' => print_r($table_desc, 1)), WATCHDOG_WARNING); 00268 00269 } 00270 foreach ($table_desc['fields'] as $field => $def) { 00271 // a field is considered missing if it cannot be NULL and there is no default 00272 // value for it or it is of type 'serial' 00273 if (array_key_exists('NOT NULL', $def) and 00274 !array_key_exists($field, $insert_values) and 00275 !array_key_exists('default', $def) and 00276 strcmp($def['type'], serial) != 0) { 00277 watchdog('tripal_core', "tripal_core_chado_insert: Field $table.$field cannot be NULL: " . 00278 print_r($values, 1), array(), 'WATCHDOG_ERROR'); 00279 return FALSE; 00280 } 00281 } 00282 } //end of validation 00283 00284 // Now build the insert SQL statement 00285 $ifields = array(); // contains the names of the fields 00286 $ivalues = array(); // contains the values of the fields 00287 $itypes = array(); // contains %d/%s placeholders for the sql query 00288 $iplaceholders = array(); // contains $1/$2 placeholders for the prepare query 00289 $idatatypes = array(); // contains the data type of the fields (int, text, etc.) 00290 $i = 1; 00291 foreach ($insert_values as $field => $value) { 00292 $ifields[] = $field; 00293 $ivalues[] = $value; 00294 $iplaceholders[] = '$' . $i; 00295 $i++; 00296 if (strcmp($value, '__NULL__')==0) { 00297 $itypes[] = "NULL"; 00298 $idatatypes[] = "NULL"; 00299 } 00300 elseif (strcasecmp($table_desc['fields'][$field]['type'], 'serial')==0 OR 00301 strcasecmp($table_desc['fields'][$field]['type'], 'int')==0 OR 00302 strcasecmp($table_desc['fields'][$field]['type'], 'integer')==0) { 00303 $itypes[] = "%d"; 00304 $idatatypes[] = 'int'; 00305 } 00306 elseif (strcasecmp($table_desc['fields'][$field]['type'], 'boolean')==0) { 00307 $itypes[] = "%s"; 00308 $idatatypes[] = 'bool'; 00309 } 00310 elseif (strcasecmp($table_desc['fields'][$field]['type'], 'float')==0) { 00311 $itypes[] = "%s"; 00312 $idatatypes[] = 'numeric'; 00313 } 00314 else { 00315 $itypes[] = "'%s'"; 00316 $idatatypes[] = 'text'; 00317 } 00318 } 00319 00320 // create the SQL 00321 $sql = "INSERT INTO {$table} (" . implode(", ", $ifields) . ") VALUES (" . implode(", ", $itypes) . ")"; 00322 00323 // if this is a prepared statement then execute it 00324 if ($prepared) { 00325 // if this is the first time we've run this query 00326 // then we need to do the prepare, otherwise just execute 00327 if ($options['is_prepared'] != TRUE) { 00328 // prepare the statement 00329 $psql = "PREPARE " . $options['statement_name'] . " (" . implode(', ', $idatatypes) . ") AS INSERT INTO {$table} (" . implode(", ", $ifields) . ") VALUES (" . implode(", ", $iplaceholders) . ")"; 00330 $status = tripal_core_chado_prepare($options['statement_name'], $psql, $idatatypes); 00331 00332 if (!$status) { 00333 watchdog('tripal_core', "tripal_core_chado_insert: not able to prepare '%name' statement for: %sql", array('%name' => $options['statement_name'], '%sql' => $sql), WATCHDOG_ERROR); 00334 return FALSE; 00335 } 00336 } 00337 00338 $sql = "EXECUTE " . $options['statement_name'] . "(" . implode(", ", $itypes) . ")"; 00339 $result = tripal_core_chado_execute_prepared($options['statement_name'], $sql, $ivalues); 00340 } 00341 // if it's not a prepared statement then insert normally 00342 else { 00343 $result = chado_query($sql, $ivalues); 00344 } 00345 00346 // if we have a result then add primary keys to return array 00347 if ($options['return_record'] == TRUE and $result) { 00348 if (array_key_exists('primary key', $table_desc) and is_array($table_desc['primary key'])) { 00349 foreach ($table_desc['primary key'] as $field) { 00350 $sql = ''; 00351 $psql = "PREPARE currval_" . $table . "_" . $field . " AS SELECT CURRVAL('" . $table . "_" . $field . "_seq')"; 00352 $is_prepared = tripal_core_chado_prepare("currval_" . $table . "_" . $field, $psql, array()); 00353 $value = ''; 00354 if ($is_prepared) { 00355 $value = db_result(chado_query("EXECUTE currval_". $table . "_" . $field)); 00356 if (!$value) { 00357 watchdog('tripal_core', "tripal_core_chado_insert: not able to retrieve primary key after insert: %sql", 00358 array('%sql' => $psql), WATCHDOG_ERROR); 00359 return FALSE; 00360 } 00361 } 00362 else { 00363 $sql = "SELECT CURRVAL('" . $table . "_" . $field . "_seq')"; 00364 $value = db_result(chado_query($sql)); 00365 if (!$value) { 00366 watchdog('tripal_core', "tripal_core_chado_insert: not able to retrieve primary key after insert: %sql", 00367 array('%sql' => $sql), WATCHDOG_ERROR); 00368 return FALSE; 00369 } 00370 } 00371 $values[$field] = $value; 00372 } 00373 } 00374 return $values; 00375 } 00376 elseif ($options['return_record'] == FALSE and $result) { 00377 return TRUE; 00378 } 00379 else { 00380 watchdog('tripal_core', "tripal_core_chado_insert: Cannot insert record into $table table: " . print_r($values, 1), array(), 'WATCHDOG_ERROR'); 00381 return FALSE; 00382 } 00383 00384 return FALSE; 00385 00386 } 00457 function tripal_core_chado_update($table, $match, $values, $options = NULL) { 00458 00459 if (!is_array($values)) { 00460 watchdog('tripal_core', 'Cannot pass non array as values for updating.', array(), 00461 WATCHDOG_ERROR); 00462 return FALSE; 00463 } 00464 if (count($values)==0) { 00465 watchdog('tripal_core', 'Cannot pass an empty array as values for updating.', array(), 00466 WATCHDOG_ERROR); 00467 return FALSE; 00468 } 00469 00470 if (!is_array($match)) { 00471 watchdog('tripal_core', 'Cannot pass non array as values for matching.', array(), 00472 WATCHDOG_ERROR); 00473 return FALSE; 00474 } 00475 if (count($match)==0) { 00476 watchdog('tripal_core', 'Cannot pass an empty array as values for matching.', array(), 00477 WATCHDOG_ERROR); 00478 return FALSE; 00479 } 00480 00481 // set defaults for options. If we don't set defaults then 00482 // we get memory leaks when we try to access the elements 00483 if (!is_array($options)) { 00484 $options = array(); 00485 } 00486 if (!array_key_exists('is_prepared', $options)) { 00487 $options['is_prepared'] = FALSE; 00488 } 00489 if (!array_key_exists('statement_name', $options)) { 00490 $options['statement_name'] = FALSE; 00491 } 00492 if (!array_key_exists('return_record', $options)) { 00493 $options['return_record'] = FALSE; 00494 } 00495 00496 $update_values = array(); // contains the values to be updated 00497 $update_matches = array(); // contains the values for the where clause 00498 00499 // Determine plan of action 00500 if ($options['statement_name']) { 00501 // we have a prepared statment (or want to create one) so set $prepared = TRUE 00502 $prepared = TRUE; 00503 00504 // we need to get a persistent connection. If one exists this function 00505 // will not recreate it, but if not it will create one and store it in 00506 // a Drupal variable for reuse later. 00507 $connection = tripal_db_persistent_chado(); 00508 00509 // if we cannot get a connection the abandon the prepared statement 00510 if (!$connection ) { 00511 $prepared = FALSE; 00512 unset($options['statement_name']); 00513 } 00514 } 00515 else { 00516 //print "NO STATEMENT (update): $table\n"; 00517 //debug_print_backtrace(); 00518 } 00519 00520 // get the table description 00521 $table_desc = tripal_core_get_chado_table_schema($table); 00522 00523 // if the user wants us to return the record then we need to get the 00524 // unique primary key if one exists. That way we can add it to the 00525 // values that get returned at the end of the function 00526 $pkeys = array(); 00527 if ($options['return_record'] == TRUE) { 00528 if (array_key_exists('primary key', $table_desc) and is_array($table_desc['primary key'])) { 00529 $columns = array(); 00530 $stmt_suffix = ''; 00531 foreach ($table_desc['primary key'] as $field) { 00532 $columns[] = $field; 00533 $stmt_suffix .= substr($field, 0, 2); 00534 } 00535 $options2 = array('statement_name' => 'sel_' . $table . '_' . $stmt_suffix); 00536 $results = tripal_core_chado_select($table, $columns, $match, $options2); 00537 if (count($results) > 0) { 00538 foreach ($results as $index => $pkey) { 00539 $pkeys[] = $pkey; 00540 } 00541 } 00542 } 00543 } 00544 00545 // get the values needed for matching in the SQL statement 00546 foreach ($match as $field => $value) { 00547 if (is_array($value)) { 00548 $foreign_options = array(); 00549 if ($options['statement_name']) { 00550 // add the fk relationship info to the prepared statement name so that 00551 // we can prepare the selects run by the recrusive tripal_core_chado_get_foreign_key 00552 // function. 00553 $fk_sname = "fk_" . $table . "_" . $field; 00554 foreach ($value as $k => $v) { 00555 $fk_sname .= substr($k, 0, 2); 00556 } 00557 $foreign_options['statement_name'] = $fk_sname; 00558 } 00559 $results = tripal_core_chado_get_foreign_key($table_desc, $field, $value, $foreign_options); 00560 if (sizeof($results) > 1) { 00561 watchdog('tripal_core', 'tripal_core_chado_update: When trying to find record to update, too many records match the criteria supplied for !foreign_key foreign key constraint (!criteria)', array('!foreign_key' => $field, '!criteria' => print_r($value, TRUE)), WATCHDOG_ERROR); 00562 } 00563 elseif (sizeof($results) < 1) { 00564 //watchdog('tripal_core', 'tripal_core_chado_update: When trying to find record to update, no record matches criteria supplied for !foreign_key foreign key constraint (!criteria)', array('!foreign_key' => $field, '!criteria' => print_r($value, TRUE)), WATCHDOG_ERROR); 00565 } 00566 else { 00567 $update_matches[$field] = $results[0]; 00568 } 00569 } 00570 else { 00571 $update_matches[$field] = $value; 00572 } 00573 } 00574 00575 // get the values used for updating 00576 foreach ($values as $field => $value) { 00577 if (is_array($value)) { 00578 $foreign_options = array(); 00579 // select the value from the foreign key relationship for this value 00580 if ($options['statement_name']) { 00581 // add the fk relationship info to the prepared statement name so that 00582 // we can prepare the selects run by the recrusive tripal_core_chado_get_foreign_key 00583 // function. 00584 $fk_sname = "fk_" . $table . "_" . $field; 00585 foreach ($value as $k => $v) { 00586 $fk_sname .= substr($k, 0, 2); 00587 } 00588 $foreign_options['statement_name'] = $fk_sname; 00589 } 00590 $results = tripal_core_chado_get_foreign_key($table_desc, $field, $value, $foreign_options); 00591 if (sizeof($results) > 1) { 00592 watchdog('tripal_core', 'tripal_core_chado_update: When trying to find update values, too many records match the criteria supplied for !foreign_key foreign key constraint (!criteria)', array('!foreign_key' => $field, '!criteria' => print_r($value, TRUE)), WATCHDOG_ERROR); 00593 } 00594 elseif (sizeof($results) < 1) { 00595 //watchdog('tripal_core', 'tripal_core_chado_update: When trying to find update values, no record matches criteria supplied for !foreign_key foreign key constraint (!criteria)', array('!foreign_key' => $field, '!criteria' => print_r($value,TRUE)), WATCHDOG_ERROR); 00596 } 00597 else { 00598 $update_values[$field] = $results[0]; 00599 } 00600 } 00601 else { 00602 $update_values[$field] = $value; 00603 } 00604 } 00605 00606 // now build the SQL statement 00607 $sql = "UPDATE {$table} SET "; 00608 $psql = "UPDATE {$table} SET "; 00609 $uargs = array(); 00610 $idatatypes = array(); 00611 $pvalues = array(); 00612 $ivalues = array(); 00613 $i = 1; 00614 foreach ($update_values as $field => $value) { 00615 00616 if (strcasecmp($table_desc['fields'][$field]['type'], 'serial')==0 OR 00617 strcasecmp($table_desc['fields'][$field]['type'], 'int')==0 OR 00618 strcasecmp($table_desc['fields'][$field]['type'], 'integer')==0) { 00619 if (strcmp($value, '__NULL__') == 0) { 00620 $sql .= " $field = %s, "; 00621 $ivalues[] = 'NULL'; 00622 $pvalues[] = '%s'; 00623 $uargs[] = 'NULL'; 00624 } 00625 else { 00626 $sql .= " $field = %d, "; 00627 $ivalues[] = $value; 00628 $pvalues[] = '%d'; 00629 $uargs[] = $value; 00630 } 00631 $idatatypes[] = 'int'; 00632 00633 } 00634 elseif (strcasecmp($table_desc['fields'][$field]['type'], 'boolean')==0) { 00635 $sql .= " $field = %s, "; 00636 $pvalues[] = '%s'; 00637 if (strcmp($value, '__NULL__')==0) { 00638 $ivalues[] = 'NULL'; 00639 $uargs[] = 'NULL'; 00640 } 00641 else { 00642 $ivalues[] = $value; 00643 $uargs[] = $value; 00644 } 00645 $idatatypes[] = 'bool'; 00646 } 00647 elseif (strcasecmp($table_desc['fields'][$field]['type'], 'float')==0) { 00648 $sql .= " $field = %s, "; 00649 $pvalues[] = '%s'; 00650 if (strcmp($value, '__NULL__')==0) { 00651 $ivalues[] = 'NULL'; 00652 $uargs[] = 'NULL'; 00653 } 00654 else { 00655 $ivalues[] = $value; 00656 $uargs[] = $value; 00657 } 00658 $idatatypes[] = 'numeric'; 00659 } 00660 else { 00661 if (strcmp($value, '__NULL__') == 0) { 00662 $sql .= " $field = %s, "; 00663 $ivalues[] = 'NULL'; 00664 $uargs[] = 'NULL'; 00665 $pvalues[] = '%s'; 00666 } 00667 else { 00668 $sql .= " $field = '%s', "; 00669 $ivalues[] = $value; 00670 $uargs[] = $value; 00671 $pvalues[] = "'%s'"; 00672 } 00673 $idatatypes[] = 'text'; 00674 } 00675 $psql .= "$field = \$" . $i . ", "; 00676 $i++; 00677 } 00678 $sql = drupal_substr($sql, 0, -2); // get rid of the trailing comma & space 00679 $psql = drupal_substr($psql, 0, -2); // get rid of the trailing comma & space 00680 00681 $sql .= " WHERE "; 00682 $psql .= " WHERE "; 00683 foreach ($update_matches as $field => $value) { 00684 00685 if (strcasecmp($table_desc['fields'][$field]['type'], 'serial')==0 OR 00686 strcasecmp($table_desc['fields'][$field]['type'], 'int')==0 OR 00687 strcasecmp($table_desc['fields'][$field]['type'], 'integer')==0) { 00688 if (strcmp($value, '__NULL__')==0) { 00689 $sql .= " $field = %s AND "; 00690 $ivalues[] = 'NULL'; 00691 $uargs[] = 'NULL'; 00692 $pvalues[] = '%s'; 00693 } 00694 else { 00695 $sql .= " $field = %d AND "; 00696 $ivalues[] = $value; 00697 $uargs[] = $value; 00698 $pvalues[] = '%s'; 00699 } 00700 $idatatypes[] = 'int'; 00701 } 00702 elseif (strcasecmp($table_desc['fields'][$field]['type'], 'boolean')==0) { 00703 $sql .= " $field = %s AND "; 00704 $pvalues[] = '%s'; 00705 if (strcmp($value, '__NULL__')==0) { 00706 $ivalues[] = 'NULL'; 00707 $uargs[] = 'NULL'; 00708 } 00709 else { 00710 $ivalues[] = $value; 00711 $uargs[] = $value; 00712 } 00713 $idatatypes[] = 'bool'; 00714 } 00715 elseif (strcasecmp($table_desc['fields'][$field]['type'], 'float')==0) { 00716 $sql .= " $field = %s AND "; 00717 $pvalues[] = '%s'; 00718 if (strcmp($value, '__NULL__')==0) { 00719 $ivalues[] = 'NULL'; 00720 $uargs[] = 'NULL'; 00721 } 00722 else { 00723 $ivalues[] = $value; 00724 $uargs[] = $value; 00725 } 00726 $idatatypes[] = 'numeric'; 00727 } 00728 else { 00729 if (strcmp($value, '__NULL__')==0) { 00730 $sql .= " $field = %s AND "; 00731 $ivalues[] = 'NULL'; 00732 $uargs[] = 'NULL'; 00733 $pvalues[] = '%s'; 00734 } 00735 else { 00736 $sql .= " $field = '%s' AND "; 00737 $ivalues[] = $value; 00738 $uargs[] = $value; 00739 $pvalues[] = "'%s'"; 00740 } 00741 $idatatypes[] = 'text'; 00742 } 00743 $psql .= "$field = \$" . $i . " AND "; 00744 $i++; 00745 } 00746 $sql = drupal_substr($sql, 0, -4); // get rid of the trailing 'AND' 00747 $psql = drupal_substr($psql, 0, -4); // get rid of the trailing 'AND' 00748 00749 // finish constructing the prepared SQL statement 00750 $psql = "PREPARE " . $options['statement_name'] . " (" . implode(', ', $idatatypes) . ") AS " . $psql; 00751 00752 // finally perform the update. If successful, return the updated record 00753 if ($prepared) { 00754 // if this is the first time we've run this query 00755 // then we need to do the prepare, otherwise just execute 00756 if ($options['is_prepared'] != TRUE and !tripal_core_is_sql_prepared($options['statement_name'])) { 00757 $status = chado_query($psql); 00758 if (!$status) { 00759 watchdog('tripal_core', "tripal_core_chado_update: not able to prepare '%name' statement for: %sql", 00760 array('%name' => $options['statement_name'], '%sql' => $sql), WATCHDOG_ERROR); 00761 return FALSE; 00762 } 00763 } 00764 $sql = "EXECUTE " . $options['statement_name'] . "(" . implode(", ", $pvalues) . ")"; 00765 $result = chado_query($sql, $ivalues); 00766 } 00767 // if it's not a prepared statement then insert normally 00768 else { 00769 $result = chado_query($sql, $uargs); 00770 } 00771 // if we have a result then add primary keys to return array 00772 if ($options['return_record'] == TRUE and $result) { 00773 // only if we have a single result do we want to add the primary keys to the values 00774 // array. If the update matched many records we can't add the pkeys 00775 00776 if (count($pkeys) == 1) { 00777 foreach ($pkeys as $index => $pkey) { 00778 foreach ($pkey as $field => $fvalue) { 00779 $values[$field] = $fvalue; 00780 } 00781 } 00782 } 00783 return $values; 00784 } 00785 elseif ($options['return_record'] == FALSE and $result) { 00786 return TRUE; 00787 } 00788 else { 00789 watchdog('tripal_core', "Cannot update record in $table table. \nMatch:" . print_r($match, 1) . "\nValues: ". print_r($values, 1), array(), 'WATCHDOG_ERROR'); 00790 return FALSE; 00791 } 00792 00793 return FALSE; 00794 } 00858 function tripal_core_chado_delete($table, $match, $options = NULL) { 00859 00860 if (!is_array($match)) { 00861 watchdog('tripal_core', 'Cannot pass non array as values for matching.', array(), 00862 WATCHDOG_ERROR); 00863 return FALSE; 00864 } 00865 if (count($match)==0) { 00866 watchdog('tripal_core', 'Cannot pass an empty array as values for matching.', array(), 00867 WATCHDOG_ERROR); 00868 return FALSE; 00869 } 00870 00871 // set defaults for options. If we don't set defaults then 00872 // we get memory leaks when we try to access the elements 00873 if (!is_array($options)) { 00874 $options = array(); 00875 } 00876 if (!array_key_exists('is_prepared', $options)) { 00877 $options['is_prepared'] = FALSE; 00878 } 00879 if (!array_key_exists('statement_name', $options)) { 00880 $options['statement_name'] = FALSE; 00881 } 00882 00883 // Determine plan of action 00884 if ($options['statement_name']) { 00885 // we have a prepared statment (or want to create one) so set $prepared = TRUE 00886 $prepared = TRUE; 00887 00888 // we need to get a persistent connection. If one exists this function 00889 // will not recreate it, but if not it will create one and store it in 00890 // a Drupal variable for reuse later. 00891 $connection = tripal_db_persistent_chado(); 00892 00893 // if we cannot get a connection the abandon the prepared statement 00894 if (!$connection ) { 00895 $prepared = FALSE; 00896 unset($options['statement_name']); 00897 } 00898 } 00899 else { 00900 //print "NO STATEMENT (update): $table\n"; 00901 //debug_print_backtrace(); 00902 } 00903 00904 $delete_matches = array(); // contains the values for the where clause 00905 00906 // get the table description 00907 $table_desc = tripal_core_get_chado_table_schema($table); 00908 $fields = $table_desc['fields']; 00909 00910 // get the values needed for matching in the SQL statement 00911 foreach ($match as $field => $value) { 00912 if (is_array($value)) { 00913 // if the user has specified an array of values to delete rather than 00914 // FK relationships the keep those in our match 00915 if (array_values($value) === $value) { 00916 $delete_matches[$field] = $value; 00917 } 00918 else { 00919 $results = tripal_core_chado_get_foreign_key($table_desc, $field, $value); 00920 if (sizeof($results) > 1) { 00921 watchdog('tripal_core', 'tripal_core_chado_delete: When trying to find record to delete, too many records match the criteria supplied for !foreign_key foreign key constraint (!criteria)', array('!foreign_key' => $field, '!criteria' => print_r($value, TRUE)), WATCHDOG_ERROR); 00922 } 00923 elseif (sizeof($results) < 1) { 00924 //watchdog('tripal_core', 'tripal_core_chado_delete: When trying to find record to delete, no record matches criteria supplied for !foreign_key foreign key constraint (!criteria)', array('!foreign_key' => $field, '!criteria' => print_r($value,TRUE)), WATCHDOG_ERROR); 00925 } 00926 else { 00927 $delete_matches[$field] = $results[0]; 00928 } 00929 } 00930 } 00931 else { 00932 $delete_matches[$field] = $value; 00933 } 00934 } 00935 00936 // now build the SQL statement 00937 $sql = "DELETE FROM {$table} WHERE "; 00938 $psql = $sql; 00939 $uargs = array(); 00940 $idatatypes = array(); 00941 $pvalues = array(); 00942 $ivalues = array(); 00943 $dargs = array(); 00944 $void_prepared = 0; 00945 $i = 1; 00946 foreach ($delete_matches as $field => $value) { 00947 00948 // if we have an array values then this is an "IN" clasue. 00949 // we cannot use prepared statements with these 00950 if (count($value) > 1) { 00951 $sql .= "$field IN (" . db_placeholders($value, 'varchar') . ") AND "; 00952 foreach ($value as $v) { 00953 $dargs[] = $v; 00954 } 00955 $void_prepared = 1; 00956 continue; 00957 } 00958 00959 if (strcasecmp($table_desc['fields'][$field]['type'], 'serial') == 0 OR 00960 strcasecmp($table_desc['fields'][$field]['type'], 'int') == 0 OR 00961 strcasecmp($table_desc['fields'][$field]['type'], 'integer') == 0) { 00962 if (strcmp($value, '__NULL__') == 0) { 00963 $sql .= " $field = NULL AND "; 00964 $ivalues[] = 'NULL'; 00965 $pvalues[] = '%s'; 00966 $uargs[] = 'NULL'; 00967 } 00968 else { 00969 $sql .= " $field = %d AND "; 00970 $ivalues[] = $value; 00971 $pvalues[] = '%d'; 00972 $uargs[] = $value; 00973 } 00974 $idatatypes[] = 'int'; 00975 } 00976 elseif (strcasecmp($table_desc['fields'][$field]['type'], 'boolean')==0) { 00977 $sql .= " $field = %s AND "; 00978 $pvalues[] = '%s'; 00979 if (strcmp($value, '__NULL__')==0) { 00980 $ivalues[] = 'NULL'; 00981 $uargs[] = 'NULL'; 00982 } 00983 else { 00984 $ivalues[] = $value; 00985 $uargs[] = $value; 00986 } 00987 $idatatypes[] = 'bool'; 00988 } 00989 elseif (strcasecmp($table_desc['fields'][$field]['type'], 'float')==0) { 00990 $sql .= " $field = %s AND "; 00991 $pvalues[] = '%s'; 00992 if (strcmp($value, '__NULL__')==0) { 00993 $ivalues[] = 'NULL'; 00994 $uargs[] = 'NULL'; 00995 } 00996 else { 00997 $ivalues[] = $value; 00998 $uargs[] = $value; 00999 } 01000 $idatatypes[] = 'numeric'; 01001 } 01002 else { 01003 if (strcmp($value, '__NULL__')==0) { 01004 $sql .= " $field = %s AND "; 01005 $ivalues[] = 'NULL'; 01006 $uargs[] = 'NULL'; 01007 $pvalues[] = '%s'; 01008 } 01009 else { 01010 $sql .= " $field = '%s' AND "; 01011 $ivalues[] = $value; 01012 $uargs[] = $value; 01013 $pvalues[] = "'%s'"; 01014 } 01015 $idatatypes[] = 'text'; 01016 } 01017 array_push($dargs, $value); 01018 $psql .= "$field = \$" . $i . " AND "; 01019 $i++; 01020 } 01021 $sql = drupal_substr($sql, 0, -4); // get rid of the trailing 'AND' 01022 $psql = drupal_substr($psql, 0, -4); // get rid of the trailing 'AND' 01023 01024 // finish constructing the prepared SQL statement 01025 $psql = "PREPARE " . $options['statement_name'] . " (" . implode(', ', $idatatypes) . ") AS " . $psql; 01026 01027 // finally perform the update. If successful, return the updated record 01028 if ($prepared and !$void_prepared) { 01029 // if this is the first time we've run this query 01030 // then we need to do the prepare, otherwise just execute 01031 if ($options['is_prepared'] != TRUE and 01032 !tripal_core_is_sql_prepared($options['statement_name'])) { 01033 $status = chado_query($psql); 01034 if (!$status) { 01035 watchdog('tripal_core', "tripal_core_chado_delete: not able to prepare '%name' statement for: %sql", array('%name' => $options['statement_name'], '%sql' => $sql), WATCHDOG_ERROR); 01036 return FALSE; 01037 } 01038 } 01039 $sql = "EXECUTE " . $options['statement_name'] . "(" . implode(", ", $pvalues) . ")"; 01040 $resource = chado_query($sql, $ivalues); 01041 } 01042 // if it's not a prepared statement then insert normally 01043 else { 01044 $resource = chado_query($sql, $uargs); 01045 } 01046 01047 // finally perform the delete. If successful, return the updated record 01048 $result = chado_query($sql, $dargs); 01049 if ($result) { 01050 return TRUE; 01051 } 01052 else { 01053 watchdog('tripal_core', "Cannot delete record in $table table. Match:" . print_r($match, 1) . ". Values: ". print_r($values, 1), array(), 'WATCHDOG_ERROR'); 01054 return FALSE; 01055 } 01056 return FALSE; 01057 } 01058 01059 01060 01159 function tripal_core_chado_select($table, $columns, $values, $options = NULL) { 01160 01161 if (!is_array($values)) { 01162 watchdog('tripal_core', 'Cannot pass non array as values for selecting.', array(), 01163 WATCHDOG_ERROR); 01164 return FALSE; 01165 } 01166 if (!is_array($columns)) { 01167 watchdog('tripal_core', 'Cannot pass non array as columns for selecting.', array(), 01168 WATCHDOG_ERROR); 01169 return FALSE; 01170 } 01171 if (count($columns)==0) { 01172 watchdog('tripal_core', 'Cannot pass an empty array as columns for selecting.', array(), 01173 WATCHDOG_ERROR); 01174 return FALSE; 01175 } 01176 01177 // set defaults for options. If we don't set defaults then 01178 // we get memory leaks when we try to access the elements 01179 if (!is_array($options)) { 01180 $options = array(); 01181 } 01182 if (!array_key_exists('case_insensitive_columns', $options)) { 01183 $options['case_insensitive_columns'] = array(); 01184 } 01185 if (!array_key_exists('regex_columns', $options)) { 01186 $options['regex_columns'] = array(); 01187 } 01188 if (!array_key_exists('order_by', $options)) { 01189 $options['order_by'] = array(); 01190 } 01191 if (!array_key_exists('is_prepared', $options)) { 01192 $options['is_prepared'] = FALSE; 01193 } 01194 if (!array_key_exists('return_sql', $options)) { 01195 $options['return_sql'] = FALSE; 01196 } 01197 if (!array_key_exists('has_record', $options)) { 01198 $options['has_record'] = FALSE; 01199 } 01200 if (!array_key_exists('statement_name', $options)) { 01201 $options['statement_name'] = FALSE; 01202 } 01203 if (!array_key_exists('is_duplicate', $options)) { 01204 $options['is_duplicate'] = FALSE; 01205 } 01206 $pager = array(); 01207 if (array_key_exists('pager', $options)) { 01208 $pager = $options['pager']; 01209 } 01210 01211 // if this is a prepared statement check to see if it has already been prepared 01212 $prepared = FALSE; 01213 if ($options['statement_name']) { 01214 $prepared = TRUE; 01215 // we need to get a persistent connection. If one exists this function 01216 // will not recreate it, but if not it will create one and store it in 01217 // a Drupal variable for reuse later. 01218 $connection = tripal_db_persistent_chado(); 01219 01220 // if we cannot get a connection the abandon the prepared statement 01221 if (!$connection) { 01222 $prepared = FALSE; 01223 unset($options['statement_name']); 01224 } 01225 } 01226 else { 01227 //print "NO STATEMENT (select): $table\n"; 01228 //debug_print_backtrace(); 01229 } 01230 01231 // check that our columns and values arguments are proper arrays 01232 if (!is_array($columns)) { 01233 watchdog('tripal_core', 'the $columns argument for tripal_core_chado_select must be an array.'); 01234 return FALSE; 01235 } 01236 if (!is_array($values)) { 01237 watchdog('tripal_core', 'the $values argument for tripal_core_chado_select must be an array.'); 01238 return FALSE; 01239 } 01240 01241 // get the table description 01242 $table_desc = tripal_core_get_chado_table_schema($table); 01243 $select = ''; 01244 $from = ''; 01245 $where = ''; 01246 $args = array(); 01247 01248 // if the 'use_unique' option is turned on then we want 01249 // to remove all but unique keys 01250 if ($options['is_duplicate'] and array_key_exists('unique keys', $table_desc)) { 01251 $ukeys = $table_desc['unique keys']; 01252 $has_results = 0; 01253 01254 // iterate through the unique constraints and reset the values and columns 01255 // arrays to only include these fields 01256 foreach ($ukeys as $cname => $fields) { 01257 if ($has_results) { 01258 continue; 01259 } 01260 $new_values = array(); 01261 $new_columns = array(); 01262 $new_options = array(); 01263 $uq_sname = "uq_" . $table . "_"; 01264 $has_pkey = 0; 01265 01266 01267 // include the primary key in the results returned 01268 if (array_key_exists('primary key', $table_desc)) { 01269 $has_pkey = 1; 01270 $pkeys = $table_desc['primary key']; 01271 foreach ($pkeys as $index => $key) { 01272 array_push($new_columns, $key); 01273 } 01274 } 01275 01276 // recreate the $values and $columns arrays 01277 foreach ($fields as $field) { 01278 if (array_key_exists($field, $values)) { 01279 $new_values[$field] = $values[$field]; 01280 $uq_sname .= substr($field, 0, 2); 01281 // if there is no primary key then use the unique contraint fields 01282 if (!$has_pkey) { 01283 array_push($new_columns, $field); 01284 } 01285 } 01286 // if the field doesn't exist in the values array then 01287 // substitute any default values 01288 elseif (array_key_exists('default', $table_desc['fields'][$field])) { 01289 $new_values[$field] = $table_desc['fields'][$field]['default']; 01290 $uq_sname .= substr($field, 0, 2); 01291 if (!$has_pkey) { 01292 array_push($new_columns, $field); 01293 } 01294 } 01295 // if there is no value (default or otherwise) check if this field is 01296 // allowed to be null 01297 elseif (!$table_desc['fields'][$field]['not null']) { 01298 $new_values[$field] = NULL; 01299 $uq_sname .= "n".substr($field, 0, 2); 01300 if (!$has_pkey) { 01301 array_push($new_columns, $field); 01302 } 01303 } 01304 // if the array key doesn't exist in the values given by the caller 01305 // and there is no default value then we cannot check if the record 01306 // is a duplicate so return FALSE 01307 else { 01308 watchdog('tripal_core', "tripal_core_chado_select: There is no value for %field" 01309 ." thus we cannot check if this record is unique", 01310 array('%field' => $field), WATCHDOG_ERROR); 01311 return FALSE; 01312 } 01313 } 01314 $new_options['statement_name'] = $uq_sname; 01315 01316 $results = tripal_core_chado_select($table, $new_columns, $new_values, $new_options); 01317 // if we have a duplicate record then return the results 01318 if (count($results) > 0) { 01319 $has_results = 1; 01320 } 01321 unset($new_columns); 01322 unset($new_values); 01323 unset($new_options); 01324 } 01325 if ($options['has_record'] and $has_results) { 01326 return TRUE; 01327 } 01328 else { 01329 return $results; 01330 } 01331 } 01332 01333 foreach ($values as $field => $value) { 01334 // make sure the field is in the table description. If not then return an error 01335 // message 01336 if (!array_key_exists($field, $table_desc['fields'])) { 01337 watchdog('tripal_core', "tripal_core_chado_select: The field '%field' does not exist ". 01338 "for the table '%table'. Cannot perform query. Values: %array", 01339 array('%field' => $field, '%table' => $table, '%array' => print_r($values, 1)), WATCHDOG_ERROR); 01340 return array(); 01341 } 01342 01343 $select[] = $field; 01344 if (is_array($value)) { 01345 // if the user has specified multiple values for matching then this we 01346 // want to catch that and save them in our $where array, otherwise 01347 // we'll descend for a foreign key relationship 01348 if (array_values($value) === $value) { 01349 $where[$field] = $value; 01350 } 01351 else { 01352 // select the value from the foreign key relationship for this value 01353 $foreign_options = array( 01354 'regex_columns' => $options['regex_columns'], 01355 'case_insensitive_columns' => $options['case_insensitive_columns'] 01356 ); 01357 if (array_key_exists('statement_name', $options) and $options['statement_name']) { 01358 // add the fk relationship info to the prepared statement name so that 01359 // we can prepare the selects run by the recrusive tripal_core_chado_get_foreign_key 01360 // function. we need the statement name to be unique so take the first two characters of each column 01361 $fk_sname = "fk_" . $table . "_" . $field; 01362 foreach ($value as $k => $v) { 01363 $fk_sname .= substr($k, 0, 2); 01364 } 01365 $foreign_options['statement_name'] = $fk_sname; 01366 } 01367 01368 $results = tripal_core_chado_get_foreign_key($table_desc, $field, $value, $foreign_options); 01369 if (!$results or count($results)==0) { 01370 01371 // foreign key records are required 01372 // thus if none matched then return FALSE and alert the admin through watchdog 01373 /*watchdog('tripal_core', 01374 'tripal_core_chado_select: no record in the table referenced by the foreign key (!field) exists. tripal_core_chado_select table=!table, columns=!columns, values=!values', 01375 array('!table' => $table, 01376 '!columns' => '<pre>' . print_r($columns, TRUE) . '</pre>', 01377 '!values' => '<pre>' . print_r($values, TRUE) . '</pre>', 01378 '!field' => $field, 01379 ), 01380 WATCHDOG_WARNING);*/ 01381 return array(); 01382 } 01383 else { 01384 $where[$field] = $results; 01385 } 01386 } 01387 } 01388 else { 01389 // need to catch a 0 and make int if integer field 01390 // but we don't want to catch a NULL 01391 if ($value === NULL) { 01392 $where[$field] = NULL; 01393 } 01394 elseif ($table_desc['fields'][$field]['type'] == 'int') { 01395 $where[$field][] = (int) $value; 01396 } 01397 else { 01398 $where[$field][] = $value; 01399 } 01400 } 01401 } 01402 01403 // now build the SQL and prepared SQL statements. We may not use 01404 // the prepared statement if it wasn't requested in the options or if the 01405 // argument in a where statement has multiple values. 01406 if (empty($where)) { 01407 // sometimes want to select everything 01408 $sql = "SELECT " . implode(', ', $columns) . " "; 01409 $sql .= "FROM {$table} "; 01410 // we don't prepare a statement if there is no where clause 01411 $prepared = FALSE; 01412 } 01413 else { 01414 $sql = "SELECT " . implode(', ', $columns) . " "; 01415 $sql .= "FROM {$table} "; 01416 01417 // if $values is empty then we want all results so no where clause 01418 if (!empty($values)) { 01419 $sql .= "WHERE "; 01420 } 01421 $psql = $sql; // prepared SQL statement; 01422 $i = 1; 01423 $pvalues = array(); 01424 $itypes = array(); 01425 foreach ($where as $field => $value) { 01426 01427 // if we have multiple values returned then we need an 'IN' statement 01428 // in our where statement 01429 if (count($value) > 1) { 01430 $sql .= "$field IN (" . db_placeholders($value, 'varchar') . ") AND "; 01431 foreach ($value as $v) { 01432 $args[] = $v; 01433 // we can't do a prepared statement with an 'IN' statement in a 01434 // where clause because we can't guarantee we'll always have the 01435 // same number of elements. 01436 $prepared = FALSE; 01437 } 01438 } 01439 // if we have a null value then we need an IS NULL in our where statement 01440 elseif ($value === NULL) { 01441 $sql .= "$field IS NULL AND "; 01442 $psql .= "$field IS NULL AND "; 01443 // Need to remove one from the argument count b/c nulls don't add an argument 01444 $i--; 01445 } 01446 // if we have a single value then we need an = in our where statement 01447 else { 01448 $operator = '='; 01449 if (in_array($field, $options['regex_columns'])) { 01450 $operator = '~*'; 01451 } 01452 01453 // get the types for the prepared statement. First check if the type 01454 // is an integer 01455 if (strcasecmp($table_desc['fields'][$field]['type'], 'serial')==0 OR 01456 strcasecmp($table_desc['fields'][$field]['type'], 'int')==0 OR 01457 strcasecmp($table_desc['fields'][$field]['type'], 'integer')==0) { 01458 $sql .= "$field $operator %d AND "; 01459 $psql .= "$field $operator \$" . $i . " AND "; 01460 $args[] = $value[0]; 01461 // set the variables needed for the prepared statement 01462 $idatatypes[] = 'int'; 01463 $itypes[] = '%d'; 01464 $pvalues[] = $value[0]; 01465 } 01466 elseif (strcasecmp($table_desc['fields'][$field]['type'], 'boolean')==0) { 01467 $sql .= "$field $operator %s AND "; 01468 $psql .= "$field $operator \$" . $i . " AND "; 01469 $args[] = $value[0]; 01470 // set the variables needed for the prepared statement 01471 $idatatypes[] = 'bool'; 01472 $itypes[] = '%d'; 01473 $pvalues[] = $value[0]; 01474 } 01475 elseif (strcasecmp($table_desc['fields'][$field]['type'], 'float')==0) { 01476 $sql .= "$field $operator %s AND "; 01477 $psql .= "$field $operator \$" . $i . " AND "; 01478 $args[] = $value[0]; 01479 // set the variables needed for the prepared statement 01480 $idatatypes[] = 'numeric'; 01481 $itypes[] = '%f'; 01482 $pvalues[] = $value[0]; 01483 } 01484 // else the type is a text 01485 else { 01486 if (in_array($field, $options['case_insensitive_columns'])) { 01487 $sql .= "lower($field) $operator lower('%s') AND "; 01488 $psql .= "lower($field) $operator lower('\$" . $i . "') AND "; 01489 $args[] = $value; 01490 } 01491 else { 01492 $sql .= "$field $operator '%s' AND "; 01493 $psql .= "$field $operator \$" . $i . " AND "; 01494 $args[] = $value[0]; 01495 } 01496 // set the variables needed for the prepared statement 01497 $idatatypes[] = 'text'; 01498 $itypes[] = "'%s'"; 01499 $pvalues[] = $value[0]; 01500 } 01501 } 01502 $i++; 01503 } // end foreach item in where clause 01504 $sql = drupal_substr($sql, 0, -4); // get rid of the trailing 'AND ' 01505 $psql = drupal_substr($psql, 0, -4); // get rid of the trailing 'AND ' 01506 01507 } // end if(empty($where)){ } else { 01508 01509 // finally add any ordering of the results to the SQL statement 01510 if (count($options['order_by']) > 0) { 01511 $sql .= " ORDER BY "; 01512 $psql .= " ORDER BY "; 01513 foreach ($options['order_by'] as $field => $dir) { 01514 $sql .= "$field $dir, "; 01515 $psql .= "$field $dir, "; 01516 } 01517 $sql = drupal_substr($sql, 0, -2); // get rid of the trailing ', ' 01518 $psql = drupal_substr($psql, 0, -2); // get rid of the trailing ', ' 01519 } 01520 01521 // finish constructing the prepared SQL statement 01522 if ($options['statement_name']) { 01523 $psql = "PREPARE " . $options['statement_name'] . " (" . implode(', ', $idatatypes) . ") AS " . $psql; 01524 } 01525 01526 // if the caller has requested the SQL rather than the results... 01527 // which happens in the case of wanting to use the Drupal pager, then do so 01528 if ($options['return_sql'] == TRUE) { 01529 return array('sql' => $sql, 'args' => $args); 01530 } 01531 01532 // prepare the statement 01533 if ($prepared) { 01534 // if this is the first time we've run this query 01535 // then we need to do the prepare, otherwise just execute 01536 if ($options['is_prepared'] != TRUE) { 01537 $status = tripal_core_chado_prepare($options['statement_name'], $psql, $idatatypes); 01538 if (!$status) { 01539 return FALSE; 01540 } 01541 } 01542 $sql = "EXECUTE " . $options['statement_name'] . "(" . implode(", ", $itypes) . ")"; 01543 // TODO: make the pager option work with prepared queries. 01544 $resource = tripal_core_chado_execute_prepared($options['statement_name'], $sql, $pvalues); 01545 } 01546 else { 01547 if (array_key_exists('limit', $pager)) { 01548 $resource = chado_pager_query($sql, $pager['limit'], $pager['element'], NULL, $args); 01549 } 01550 else { 01551 $resource = chado_query($sql, $args); 01552 } 01553 } 01554 01555 // format results into an array 01556 $results = array(); 01557 while ($r = db_fetch_object($resource)) { 01558 $results[] = $r; 01559 } 01560 if ($options['has_record']) { 01561 return count($results); 01562 } 01563 return $results; 01564 } 01565 01614 function tripal_core_chado_get_foreign_key($table_desc, $field, $values, $options = NULL) { 01615 01616 // set defaults for options. If we don't set defaults then 01617 // we get memory leaks when we try to access the elements 01618 if (!is_array($options)) { 01619 $options = array(); 01620 } 01621 if (!array_key_exists('case_insensitive_columns', $options)) { 01622 $options['case_insensitive_columns'] = array(); 01623 } 01624 if (!array_key_exists('regex_columns', $options)) { 01625 $options['regex_columns'] = array(); 01626 } 01627 01628 // get the list of foreign keys for this table description and 01629 // iterate through those until we find the one we're looking for 01630 $fkeys = ''; 01631 if (array_key_exists('foreign keys', $table_desc)) { 01632 $fkeys = $table_desc['foreign keys']; 01633 } 01634 if ($fkeys) { 01635 foreach ($fkeys as $name => $def) { 01636 if (is_array($def['table'])) { 01637 //foreign key was described 2X 01638 $message = "The foreign key " . $name . " was defined twice. Please check modules " 01639 ."to determine if hook_chado_schema_<version>_" . $table_desc['table'] . "() was " 01640 ."implemented and defined this foreign key when it wasn't supposed to. Modules " 01641 ."this hook was implemented in: " . implode(', ', 01642 module_implements("chado_" . $table_desc['table'] . "_schema")) . "."; 01643 watchdog('tripal_core', $message); 01644 drupal_set_message(check_plain($message), 'error'); 01645 continue; 01646 } 01647 $table = $def['table']; 01648 $columns = $def['columns']; 01649 01650 // iterate through the columns of the foreign key relationship 01651 foreach ($columns as $left => $right) { 01652 // does the left column in the relationship match our field? 01653 if (strcmp($field, $left) == 0) { 01654 // the column name of the foreign key matches the field we want 01655 // so this is the right relationship. Now we want to select 01656 $select_cols = array($right); 01657 $result = tripal_core_chado_select($table, $select_cols, $values, $options); 01658 $fields = array(); 01659 if ($result and count($result) > 0) { 01660 foreach ($result as $obj) { 01661 $fields[] = $obj->$right; 01662 } 01663 return $fields; 01664 } 01665 } 01666 } 01667 } 01668 } 01669 else { 01670 // TODO: what do we do if we get to this point and we have a fk 01671 // relationship expected but we don't have any definition for one in the 01672 // table schema?? 01673 $version = tripal_core_get_chado_version(TRUE); 01674 $message = t("There is no foreign key relationship defined for " . $field . ". 01675 To define a foreign key relationship, determine the table this foreign 01676 key referrs to (<foreign table>) and then implement 01677 hook_chado_chado_schema_v<version>_<foreign table>(). See 01678 tripal_feature_chado_v1_2_schema_feature for an example. Chado version: $version"); 01679 watchdog('tripal_core', $message); 01680 drupal_set_message(check_plain($message), 'error'); 01681 } 01682 01683 return array(); 01684 } 01685 01777 function tripal_core_generate_chado_var($table, $values, $base_options = array()) { 01778 $all = new stdClass(); 01779 01780 $return_array = 0; 01781 if (array_key_exists('return_array', $base_options)) { 01782 $return_array = 1; 01783 } 01784 $include_fk = 0; 01785 if (array_key_exists('include_fk', $base_options)) { 01786 $include_fk = $base_options['include_fk']; 01787 } 01788 $pager = array(); 01789 if (array_key_exists('pager', $base_options)) { 01790 $pager = $base_options['pager']; 01791 } 01792 // get description for the current table---------------------------------------------------------- 01793 $table_desc = tripal_core_get_chado_table_schema($table); 01794 if (!$table_desc or count($table_desc) == 0) { 01795 watchdog('tripal_core', "tripal_core_generate_chado_var: The table '%table' has not been defined. ". 01796 "and cannot be expanded. If this is a custom table, please add it using the Tripal ". 01797 "custom table interface.", array('%table' => $table), WATCHDOG_ERROR); 01798 if ($return_array) { 01799 return array(); 01800 } 01801 return FALSE; 01802 } 01803 $table_primary_key = $table_desc['primary key'][0]; 01804 $table_columns = array_keys($table_desc['fields']); 01805 01806 // Expandable fields without value needed for criteria-------------------------------------------- 01807 $all->expandable_fields = array(); 01808 if ($table_desc['referring_tables']) { 01809 $all->expandable_tables = $table_desc['referring_tables']; 01810 } 01811 else { 01812 $all->expandable_tables = array(); 01813 } 01814 $all->expandable_nodes = array(); 01815 01816 // Get fields to be removed by name................................. 01817 $fields_to_remove = module_invoke_all('exclude_field_from_' . $table . '_by_default'); 01818 foreach ($fields_to_remove as $field_name => $criteria) { 01819 //replace >field_name< with the current field name & 01820 $criteria = preg_replace('/>field_name< /', addslashes($field_name), $criteria); 01821 // if field_value needed we can't deal with this field yet 01822 if (preg_match('/>field_value< /', $criteria)) { 01823 break; 01824 } 01825 01826 //if criteria then remove from query 01827 // @coder-ignore: only module designers can populate $criteria -not security risk 01828 $success = drupal_eval('<?php return ' . $criteria . '; ?>'); 01829 // watchdog('tripal_core', 01830 // 'Evaluating criteria (%criteria) for field %field in tripal_core_generate_chado_var for %table evaluated to %success', 01831 // array('%table' => $table, '%criteria'=>$criteria, '%field' => $field_name, '%success'=>$success), 01832 // WATCHDOG_NOTICE 01833 // ); 01834 if ($success) { 01835 unset($table_columns[array_search($field_name, $table_columns)]); 01836 unset($fields_to_remove[$field_name]); 01837 $all->expandable_fields[] = $table . '.' . $field_name; 01838 } 01839 } 01840 01841 //Get fields to be removed by type................................ 01842 $types_to_remove = module_invoke_all('exclude_type_by_default'); 01843 $field_types = array(); 01844 foreach ($table_desc['fields'] as $field_name => $field_array) { 01845 $field_types[$field_array['type']][] = $field_name; 01846 } 01847 foreach ($types_to_remove as $field_type => $criteria) { 01848 // if there are fields of that type to remove 01849 if (is_array($field_types[$field_type])) { 01850 //replace >field_name< with the current field name & 01851 $criteria = preg_replace('/>field_name< /', addslashes($field_name), $criteria); 01852 foreach ($field_types[$field_type] as $field_name) { 01853 // if field_value needed we can't deal with this field yet 01854 if (preg_match('/>field_value< /', $criteria)) { 01855 $fields_to_remove[$field_name] = $criteria; 01856 continue; 01857 } 01858 // if field_value needed we can't deal with this field yet 01859 if (preg_match('/>field_value< /', $criteria)) { 01860 break; 01861 } 01862 //if criteria then remove from query 01863 // @coder-ignore: only module designers can populate $criteria -not security risk 01864 $success = drupal_eval('<?php return ' . $criteria . '; ?>'); 01865 // watchdog('tripal_core', 01866 // 'Evaluating criteria (%criteria) for field %field of $type in tripal_core_generate_chado_var for %table evaluated to %success', 01867 // array('%table'=>$table, '%criteria'=>$criteria, '%field'=>$field_name, '%type'=>$field_type, '%success'=>$success), 01868 // WATCHDOG_NOTICE 01869 // ); 01870 if ($success) { 01871 unset($table_columns[array_search($field_name, $table_columns)]); 01872 $all->expandable_fields[] = $table . '.' . $field_name; 01873 } 01874 } //end of foreach field of that type 01875 } 01876 } //end of foreach type to be removed 01877 01878 // get the values for the record in the current table--------------------------------------------- 01879 $results = tripal_core_chado_select($table, $table_columns, $values, $base_options); 01880 if ($results) { 01881 foreach ($results as $key => $object) { 01882 // Add empty expandable_x arrays 01883 $object->expandable_fields = $all->expandable_fields; 01884 $object->expandable_tables = $all->expandable_tables; 01885 $object->expandable_nodes = $all->expandable_nodes; 01886 // add curent table 01887 $object->tablename = $table; 01888 01889 // check if the current table maps to a node type----------------------------------------------- 01890 // if this table is connected to a node there will be a chado_tablename table in drupal 01891 if (db_table_exists('chado_' . $table)) { 01892 // that has a foreign key to this one ($table_desc['primary key'][0] 01893 // and to the node table (nid) 01894 $sql = "SELECT %s, nid FROM {chado_%s} WHERE %s=%d"; 01895 $mapping = db_fetch_object(db_query($sql, $table_primary_key, $table, 01896 $table_primary_key, $object->{$table_primary_key})); 01897 if ($mapping->{$table_primary_key}) { 01898 $object->nid = $mapping->nid; 01899 $object->expandable_nodes[] = $table; 01900 } 01901 } 01902 01903 // remove any fields where criteria need to be evalulated--------------------------------------- 01904 foreach ($fields_to_remove as $field_name => $criteria) { 01905 if (!isset($object->{$field_name})) { 01906 break; 01907 } 01908 $criteria = preg_replace('/>field_value< /', addslashes($object->{$field_name}), $criteria); 01909 //if criteria then remove from query 01910 // @coder-ignore: only module designers can populate $criteria -not security risk 01911 $success = drupal_eval('<?php return ' . $criteria . '; ?>'); 01912 // watchdog('tripal_core', 01913 // 'Evaluating criteria (%criteria) for field %field in tripal_core_generate_chado_var for %table evaluated to %success', 01914 // array('%table' => $table, '%criteria'=>$criteria, '%field' => $field_name, '%success'=>$success), 01915 // WATCHDOG_NOTICE 01916 // ); 01917 if ($success) { 01918 unset($object->{$field_name}); 01919 $object->expandable_fields[] = $table . '.' . $field_name; 01920 } 01921 } 01922 01923 // recursively follow foreign key relationships nesting objects as we go------------------------ 01924 if ($table_desc['foreign keys']) { 01925 foreach ($table_desc['foreign keys'] as $foreign_key_array) { 01926 $foreign_table = $foreign_key_array['table']; 01927 foreach ($foreign_key_array['columns'] as $foreign_key => $primary_key) { 01928 // Note: Foreign key is the field in the current table whereas primary_key is the field in 01929 // the table referenced by the foreign key 01930 //Dont do anything if the foreign key is empty 01931 if (empty($object->{$foreign_key})) { 01932 continue; 01933 } 01934 01935 if ($include_fk) { 01936 // don't recurse if the callee has supplied an $fk_include list and this 01937 // FK table is not in the list. 01938 if (is_array($include_fk) and !array_key_exists($foreign_key, $include_fk)) { 01939 continue; 01940 } 01941 // if we have the option but it is not an array then we don't recurse any furutehr 01942 if (!is_array($include_fk)) { 01943 continue; 01944 } 01945 } 01946 // get the record from the foreign table 01947 $foreign_values = array($primary_key => $object->{$foreign_key}); 01948 $options = array(); 01949 if (is_array($include_fk)) { 01950 $options['include_fk'] = $include_fk[$foreign_key]; 01951 } 01952 $foreign_object = tripal_core_generate_chado_var($foreign_table, $foreign_values, $options); 01953 // add the foreign record to the current object in a nested manner 01954 $object->{$foreign_key} = $foreign_object; 01955 // Flatten expandable_x arrays so only in the bottom object 01956 if (is_array($object->{$foreign_key}->expandable_fields)) { 01957 $object->expandable_fields = array_merge( 01958 $object->expandable_fields, 01959 $object->{$foreign_key}->expandable_fields 01960 ); 01961 unset($object->{$foreign_key}->expandable_fields); 01962 } 01963 if (is_array($object->{$foreign_key}->expandable_tables)) { 01964 $object->expandable_tables = array_merge( 01965 $object->expandable_tables, 01966 $object->{$foreign_key}->expandable_tables 01967 ); 01968 unset($object->{$foreign_key}->expandable_tables); 01969 } 01970 if (is_array($object->{$foreign_key}->expandable_nodes)) { 01971 $object->expandable_nodes = array_merge( 01972 $object->expandable_nodes, 01973 $object->{$foreign_key}->expandable_nodes 01974 ); 01975 unset($object->{$foreign_key}->expandable_nodes); 01976 } 01977 } 01978 } 01979 $results[$key] = $object; 01980 } 01981 } 01982 } 01983 01984 // check only one result returned 01985 if (!$return_array) { 01986 if (sizeof($results) == 1) { 01987 // add results to object 01988 return $results[0]; 01989 } 01990 elseif (!empty($results)) { 01991 return $results; 01992 } 01993 else { 01994 // no results returned 01995 } 01996 } 01997 // the caller has requested results are always returned as 01998 // an array 01999 else { 02000 if (!$results) { 02001 return array(); 02002 } 02003 else { 02004 return $results; 02005 } 02006 } 02007 } 02008 02082 function tripal_core_expand_chado_vars($object, $type, $to_expand, $table_options = array()) { 02083 02084 // make sure we have a value 02085 if (!$object) { 02086 watchdog('tripal_core', 'Cannot pass non array as $object.', array(),WATCHDOG_ERROR); 02087 return $object; 02088 } 02089 02090 // check to see if we are expanding an array of objects 02091 if (is_array($object)) { 02092 foreach ($object as $index => $o) { 02093 $object[$index] = tripal_core_expand_chado_vars($o, $type, $to_expand); 02094 } 02095 return $object; 02096 } 02097 02098 // get the base table name 02099 $base_table = $object->tablename; 02100 02101 switch ($type) { 02102 case "field": //-------------------------------------------------------------------------------- 02103 if (preg_match('/(\w+)\.(\w+)/', $to_expand, $matches)) { 02104 $tablename = $matches[1]; 02105 $fieldname = $matches[2]; 02106 $table_desc = tripal_core_get_chado_table_schema($tablename); 02107 $values = array(); 02108 foreach ($table_desc['primary key'] as $key) { 02109 $values[$key] = $object->{$key}; 02110 } 02111 if ($base_table == $tablename) { 02112 //get the field 02113 $results = tripal_core_chado_select($tablename, array($fieldname), $values); 02114 $object->{$fieldname} = $results[0]->{$fieldname}; 02115 $object->expanded = $to_expand; 02116 } 02117 else { 02118 //We need to recurse -the field is in a nested object 02119 foreach ((array) $object as $field_name => $field_value) { 02120 if (is_object($field_value)) { 02121 $object->{$field_name} = tripal_core_expand_chado_vars( 02122 $field_value, 02123 'field', 02124 $to_expand 02125 ); 02126 } 02127 } //end of for each field in the current object 02128 } 02129 } 02130 else { 02131 watchdog('tripal_core', 'tripal_core_expand_chado_vars: Field (%field) not in the right format. ". 02132 "It should be <tablename>.<fieldname>', WATCHDOG_ERROR); 02133 } 02134 break; 02135 case "table": //-------------------------------------------------------------------------------- 02136 $foreign_table = $to_expand; 02137 02138 // don't expand the table it already is expanded 02139 if (array_key_exists($foreign_table, $object)) { 02140 return $object; 02141 } 02142 $foreign_table_desc = tripal_core_get_chado_table_schema($foreign_table); 02143 02144 // If it's connected to the base table via a FK constraint 02145 if ($foreign_table_desc['foreign keys'][$base_table]) { 02146 foreach ($foreign_table_desc['foreign keys'][$base_table]['columns'] as $left => $right) { 02147 // if the FK value in the base table is not there then we can't expand it, so just skip it. 02148 if (!$object->{$right}) { 02149 continue; 02150 } 02151 02152 // generate a new object for this table using the FK values in the base table. 02153 // if a prepared statement is provided generate a new statement_name so that 02154 // we don't conflict when we recurse. 02155 $new_options = $table_options; 02156 if (array_key_exists('statement_name', $table_options)) { 02157 $new_options['statement_name'] = "exp_" . $foreign_table . "_" . substr($left, 0, 2) . substr($right, 0, 2); 02158 } 02159 $foreign_object = tripal_core_generate_chado_var($foreign_table, array($left => $object->{$right}), $new_options); 02160 02161 // if the generation of the object was successful, update the base object to include it. 02162 if ($foreign_object) { 02163 // in the case where the foreign key relationships exists more 02164 // than once with the same table we want to alter the array structure. rather than 02165 // add the object with a key of the table name, we will add the FK field name in between 02166 if (count($foreign_table_desc['foreign keys'][$base_table]['columns']) > 1) { 02167 if (!is_object($object->{$foreign_table})) { 02168 $object->{$foreign_table} = new stdClass(); 02169 } 02170 $object->{$foreign_table}->{$left} = $foreign_object; 02171 $object->expanded = $to_expand; 02172 } 02173 else { 02174 $object->{$foreign_table} = $foreign_object; 02175 $object->expanded = $to_expand; 02176 } 02177 } 02178 // if the object returned is NULL then handle that 02179 else { 02180 if (count($foreign_table_desc['foreign keys'][$base_table]['columns']) > 1) { 02181 $object->{$foreign_table}->{$left} = NULL; 02182 } 02183 else { 02184 $object->{$foreign_table} = NULL; 02185 } 02186 } 02187 } 02188 } 02189 // if the foreign table is not connected to the base table through a FK constraint 02190 else { 02191 // We need to recurse -the table has a relationship to one of the nested objects 02192 $did_expansion = 0; 02193 foreach ((array) $object as $field_name => $field_value) { 02194 // if we have a nested object ->expand the table in it 02195 if (is_object($field_value)) { 02196 $did_expansion = 1; 02197 $object->{$field_name} = tripal_core_expand_chado_vars($field_value, 'table', $foreign_table); 02198 } 02199 } 02200 // if we did not expand this table we should return a message that the foreign table 02201 // could not be expanded 02202 if (!$did_expansion) { 02203 watchdog('tripal_core', 'tripal_core_expand_chado_vars: Could not expand table, %table. It is ', 02204 'not in a foreign key relationship with the base object nor with any other expanded table. ' . 02205 'Check the table definition to ensure that a proper foreign key relationship is present.', 02206 array('%table' => $foreign_table), WATCHDOG_ERROR); 02207 } 02208 } 02209 break; 02210 case "node": //--------------------------------------------------------------------------------- 02211 //if the node to be expanded is for our base table, then just expand it 02212 if ($object->tablename == $to_expand) { 02213 $node = node_load($object->nid); 02214 if ($node) { 02215 $object->expanded = $to_expand; 02216 $node->expandable_fields = $object->expandable_fields; 02217 unset($object->expandable_fields); 02218 $node->expandable_tables = $object->expandable_tables; 02219 unset($object->expandable_tables); 02220 $node->expandable_nodes = $object->expandable_nodes; 02221 unset($object->expandable_nodes); 02222 $node->{$base_table} = $object; 02223 $object = $node; 02224 } 02225 else { 02226 watchdog('tripal_core', 'tripal_core_expand_chado_vars: No node matches the nid (%nid) supplied.', 02227 array('%nid' => $object->nid), WATCHDOG_ERROR); 02228 } //end of if node 02229 } 02230 else { 02231 //We need to recurse -the node to expand is one of the nested objects 02232 foreach ((array) $object as $field_name => $field_value) { 02233 if (is_object($field_value)) { 02234 $object->{$field_name} = tripal_core_expand_chado_vars( 02235 $field_value, 02236 'node', 02237 $to_expand 02238 ); 02239 } 02240 } //end of for each field in the current object 02241 } 02242 break; 02243 default: 02244 watchdog('tripal_core', 'tripal_core_expand_chado_vars: Unrecognized type (%type). Should be one of "field", "table", "node".', 02245 array('%type' => $type), WATCHDOG_ERROR); 02246 return FALSE; 02247 } 02248 02249 //move extended array downwards------------------------------------------------------------------- 02250 if (!$object->expanded) { 02251 //if there's no extended field then go hunting for it 02252 foreach ( (array)$object as $field_name => $field_value) { 02253 if (is_object($field_value)) { 02254 if (isset($field_value->expanded)) { 02255 $object->expanded = $field_value->expanded; 02256 unset($field_value->expanded); 02257 } 02258 } 02259 } 02260 } 02261 //try again becasue now we might have moved it down 02262 if ($object->expanded) { 02263 $expandable_name = 'expandable_' . $type . 's'; 02264 if ($object->{$expandable_name}) { 02265 $key_to_remove = array_search($object->expanded, $object->{$expandable_name}); 02266 unset($object->{$expandable_name}[$key_to_remove]); 02267 unset($object->expanded); 02268 } 02269 else { 02270 // if there is an expandable array then we've reached the base object 02271 // if we get here and don't have anything expanded then something went wrong 02272 // watchdog( 02273 // 'tripal_core', 02274 // 'tripal_core_expand_chado_vars: Unable to expand the %type %to_expand', 02275 // array('%type'=>$type, '%to_expand'=>$to_expand), 02276 // WATCHDOG_ERROR 02277 // ); 02278 } //end of it we've reached the base object 02279 } 02280 02281 return $object; 02282 } 02283 02309 function tripal_core_exclude_type_by_default() { 02310 return array('text' => 'strlen(">field_value< ") > 100'); 02311 } 02312 02338 function tripal_core_exclude_field_from_feature_by_default() { 02339 return array(); 02340 } 02341 02360 function chado_pager_query($query, $limit, $element, $count) { 02361 02362 // The following code is almost an exact duplicate of the 02363 // Drupal pager_query function. However, substitions have 02364 // been made to call chado_query rather than db_query 02365 02366 global $pager_page_array, $pager_total, $pager_total_items; 02367 $page = isset($_GET['page']) ? $_GET['page'] : ''; 02368 02369 // Substitute in query arguments. 02370 $args = func_get_args(); 02371 $args = array_slice($args, 4); 02372 // Alternative syntax for '...' 02373 if (isset($args[0]) && is_array($args[0])) { 02374 $args = $args[0]; 02375 } 02376 02377 // Construct a count query if none was given. 02378 if (!isset($count_query)) { 02379 $count_query = preg_replace(array('/SELECT.*?FROM /As', '/ORDER BY .*/'), array('SELECT COUNT(*) FROM ', ''), $query); 02380 } 02381 02382 // Convert comma-separated $page to an array, used by other functions. 02383 $pager_page_array = explode(',', $page); 02384 02385 // We calculate the total of pages as ceil(items / limit). 02386 $pager_total_items[$element] = db_result(chado_query($count_query, $args)); 02387 $pager_total[$element] = ceil($pager_total_items[$element] / $limit); 02388 $pager_page_array[$element] = max(0, min((int) $pager_page_array[$element], ((int) $pager_total[$element]) - 1)); 02389 02390 return chado_query_range($query, $args, $pager_page_array[$element] * $limit, $limit); 02391 } 02392 02410 function chado_query_range($query) { 02411 $args = func_get_args(); 02412 $count = array_pop($args); 02413 $from = array_pop($args); 02414 array_shift($args); 02415 02416 $query = db_prefix_tables($query); 02417 if (isset($args[0]) and is_array($args[0])) { // 'All arguments in one array' syntax 02418 $args = $args[0]; 02419 } 02420 _db_query_callback($args, TRUE); 02421 $query = preg_replace_callback(DB_QUERY_REGEXP, '_db_query_callback', $query); 02422 $query .= ' LIMIT ' . (int) $count . ' OFFSET ' . (int) $from; 02423 return chado_query($query); 02424 } 02440 function chado_query($sql) { 02441 global $persistent_chado; 02442 02443 $is_local = tripal_core_is_chado_local(); 02444 02445 $args = func_get_args(); 02446 array_shift($args); // remove the $sql from the argument list 02447 $sql = db_prefix_tables($sql); 02448 if (isset($args[0]) and is_array($args[0])) { // 'All arguments in one array' syntax 02449 $args = $args[0]; 02450 } 02451 02452 // run the Drupal command to clean up the SQL 02453 _db_query_callback($args, TRUE); 02454 $sql = preg_replace_callback(DB_QUERY_REGEXP, '_db_query_callback', $sql); 02455 02456 // add the chado schema to the table names if Chado is local to the Drupal database 02457 if ($is_local) { 02458 // TODO: this regular expression really needs to be removed as there are too many 02459 // cases where it could break. Instead we need to surround tables with brackets 02460 // like Drupal tables are and then find those and prefix those with chado. 02461 $sql = preg_replace('/\n/', '', $sql); // remove carriage returns 02462 // in the statement below we want to add 'chado.' to the beginning of each table 02463 // we use the FROM keyword to look for tables, but FROM is also used in the 02464 // 'substring' function of postgres. But since table names can't start with 02465 // a number we exclude words numeric values. We also exclude tables that 02466 // already have a schema prefix. 02467 $sql = preg_replace('/FROM\s+([^0123456789\(][^\.]*?)(\s|$)/i', 'FROM chado.\1 ', $sql); 02468 $sql = preg_replace('/INNER\s+JOIN\s+([^\.]*?)\s/i', 'INNER JOIN chado.\1 ', $sql); 02469 } 02470 //print "$sql\n"; 02471 02472 // If the query is not a select then we still need to change the search_path 02473 if (!preg_match('/^SELECT/i',$sql)) { 02474 $change_path = TRUE; 02475 } 02476 02477 // Execute the query on the chado database/schema 02478 // Use the persistent chado connection if it already exists 02479 if ($persistent_chado) { 02480 02481 $query = $sql; 02482 // Duplicate the _db_query code in order to ensure that the drupal 02483 // $active_db variable is not used in the pg_query command 02484 // thus changed $active_db to $persistent_chado 02485 // START COPY FROM _db_query in database.pgsql.inc 02486 if (variable_get('dev_query', 0)) { 02487 list($usec, $sec) = explode(' ', microtime()); 02488 $timer = (float) $usec + (float) $sec; 02489 } 02490 // if we're local we can just run the query 02491 if ($is_local) { 02492 if ($change_path) { 02493 $previous_db = tripal_db_set_active('chado'); 02494 } 02495 $last_result = pg_query($persistent_chado, $query); 02496 if ($change_path) { 02497 tripal_db_set_active($previous_db); 02498 } 02499 } 02500 else { 02501 $previous_db = tripal_db_set_active('chado'); 02502 $last_result = pg_query($persistent_chado, $query); 02503 tripal_db_set_active($previous_db); 02504 } 02505 02506 if (variable_get('dev_query', 0)) { 02507 $bt = debug_backtrace(); 02508 $query = $bt[2]['function'] . "\n" . $query; 02509 list($usec, $sec) = explode(' ', microtime()); 02510 $stop = (float) $usec + (float) $sec; 02511 $diff = $stop - $timer; 02512 $queries[] = array($query, $diff); 02513 } 02514 02515 if ($last_result !== FALSE) { 02516 return $last_result; 02517 } 02518 else { 02519 // Indicate to drupal_error_handler that this is a database error. 02520 ${DB_ERROR} = TRUE; 02521 trigger_error(check_plain(pg_last_error($persistent_chado) . "\nquery: " . $query), E_USER_WARNING); 02522 return FALSE; 02523 } 02524 // END COPY FROM _db_query in database.pgsql.inc 02525 } 02526 else { 02527 // before running the query we want to prefix the table names with 02528 // the chado schema. Previously use had to make changes to the 02529 // search_path but that caused a lot of database calls and wasted 02530 // resources during long jobs. 02531 if ($is_local) { 02532 if ($change_path) { 02533 $previous_db = tripal_db_set_active('chado'); 02534 } 02535 $results = _db_query($sql); 02536 if ($change_path) { 02537 tripal_db_set_active($previous_db); 02538 } 02539 } 02540 else { 02541 $previous_db = tripal_db_set_active('chado') ; 02542 $results = _db_query($sql); 02543 tripal_db_set_active($previous_db); 02544 } 02545 } 02546 return $results; 02547 } 02548 02559 function chado_get_id_for_node($table, $node) { 02560 return db_result(db_query("SELECT %s_id FROM {chado_%s} WHERE nid = %d", $table, $table, $node->nid)); 02561 } 02562 02573 function chado_get_node_id($table, $id) { 02574 return db_result(db_query("SELECT nid FROM {chado_%s} WHERE %s_id = %d", $table, $table, $id)); 02575 } 02576 02598 function tripal_core_get_property($basetable, $record_id, $property, $cv_name) { 02599 // get the foreign key for this property table 02600 $table_desc = tripal_core_get_chado_table_schema($basetable . 'prop'); 02601 $fkcol = key($table_desc['foreign keys'][$basetable]['columns']); 02602 02603 // construct the array of values to be selected 02604 $values = array( 02605 $fkcol => $record_id, 02606 'type_id' => array( 02607 'cv_id' => array( 02608 'name' => $cv_name, 02609 ), 02610 'name' => $property, 02611 'is_obsolete' => 0 02612 ), 02613 ); 02614 $results = tripal_core_generate_chado_var($basetable . 'prop', $values); 02615 $results = tripal_core_expand_chado_vars($results, 'field', $basetable . 'prop.value'); 02616 02617 return $results; 02618 } 02619 02647 function tripal_core_insert_property($basetable, $record_id, $property, 02648 $cv_name, $value, $update_if_present = 0) { 02649 02650 // first see if the property already exists, if the user want's to update 02651 // then we can do that, but otherwise we want to increment the rank and 02652 // insert 02653 $props = tripal_core_get_property($basetable, $record_id, $property, $cv_name); 02654 if (!is_array($props)) { 02655 $props = array($props); 02656 } 02657 $rank = 0; 02658 if (count($props) > 0) { 02659 if ($update_if_present) { 02660 return tripal_core_update_property($basetable, $record_id, $property, $cv_name, $value); 02661 } 02662 else { 02663 // iterate through the properties returned and check to see if the 02664 // property with this value already exists if not, get the largest rank 02665 // and insert the same property but with this new value 02666 foreach ($props as $p) { 02667 if ($p->rank > $rank) { 02668 $rank = $p->rank; 02669 } 02670 if (strcmp($p->value, $value) == 0) { 02671 return TRUE; 02672 } 02673 } 02674 // now add 1 to the rank 02675 $rank++; 02676 } 02677 } 02678 else { 02679 watchdog('tripal_core', "Cannot find property '!prop_name'.", 02680 array('!prop_name' => $property), WATCHDOG_ERROR); 02681 } 02682 02683 02684 // get the foreign key for this property table 02685 $table_desc = tripal_core_get_chado_table_schema($basetable . 'prop'); 02686 $fkcol = key($table_desc['foreign keys'][$basetable]['columns']); 02687 02688 // construct the array of values to be inserted 02689 $values = array( 02690 $fkcol => $record_id, 02691 'type_id' => array( 02692 'cv_id' => array( 02693 'name' => $cv_name, 02694 ), 02695 'name' => $property, 02696 'is_obsolete' => 0 02697 ), 02698 'value' => $value, 02699 'rank' => $rank, 02700 ); 02701 02702 return tripal_core_chado_insert($basetable . 'prop', $values); 02703 } 02704 02735 function tripal_core_update_property($basetable, $record_id, $property, 02736 $cv_name, $value, $insert_if_missing = 0) { 02737 02738 // first see if the property is missing (we can't update a missing property 02739 $prop = tripal_core_get_property($basetable, $record_id, $property, $cv_name); 02740 if (count($prop)==0) { 02741 if ($insert_if_missing) { 02742 return tripal_core_insert_property($basetable, $record_id, $property, $cv_name, $value); 02743 } 02744 else { 02745 return FALSE; 02746 } 02747 } 02748 02749 // get the foreign key for this property table 02750 $table_desc = tripal_core_get_chado_table_schema($basetable . 'prop'); 02751 $fkcol = key($table_desc['foreign keys'][$basetable]['columns']); 02752 02753 // construct the array that will match the exact record to update 02754 $match = array( 02755 $fkcol => $record_id, 02756 'type_id' => array( 02757 'cv_id' => array( 02758 'name' => $cv_name, 02759 ), 02760 'name' => $property, 02761 ), 02762 ); 02763 02764 // construct the array of values to be updated 02765 $values = array( 02766 'value' => $value, 02767 ); 02768 02769 return tripal_core_chado_update($basetable . 'prop', $match, $values); 02770 } 02771 02796 function tripal_core_update_property_by_id($basetable, $record_id, $property, 02797 $cv_name, $value) { 02798 02799 // get the primary key for this property table 02800 $table_desc = tripal_core_get_chado_table_schema($basetable . 'prop'); 02801 $pkcol = $table_desc['primary key'][0]; 02802 02803 // construct the array that will match the exact record to update 02804 $match = array( 02805 $pkcol => $record_id, 02806 ); 02807 02808 // construct the array of values to be updated 02809 $values = array( 02810 'type_id' => array( 02811 'cv_id' => array( 02812 'name' => $cv_name, 02813 ), 02814 'name' => $property, 02815 ), 02816 'value' => $value, 02817 ); 02818 02819 return tripal_core_chado_update($basetable . 'prop', $match, $values); 02820 } 02821 02842 function tripal_core_delete_property($basetable, $record_id, $property, $cv_name) { 02843 02844 // get the foreign key for this property table 02845 $table_desc = tripal_core_get_chado_table_schema($basetable . 'prop'); 02846 $fkcol = key($table_desc['foreign keys'][$basetable]['columns']); 02847 02848 // construct the array that will match the exact record to update 02849 $match = array( 02850 $fkcol => $record_id, 02851 'type_id' => array( 02852 'cv_id' => array( 02853 'name' => $cv_name, 02854 ), 02855 'name' => $property, 02856 ), 02857 ); 02858 02859 return tripal_core_chado_delete($basetable . 'prop', $match); 02860 } 02861 02876 function tripal_core_delete_property_by_id($basetable, $record_id) { 02877 02878 // get the foreign key for this property table 02879 $table_desc = tripal_core_get_chado_table_schema($basetable . 'prop'); 02880 $pkcol = $table_desc['primary key'][0]; 02881 02882 // construct the array that will match the exact record to update 02883 $match = array( 02884 $pkcol => $record_id, 02885 ); 02886 02887 return tripal_core_chado_delete($basetable . 'prop', $match); 02888 } 02889 02890 02891 02892 02893 02904 function tripal_db_set_active($dbname = 'default') { 02905 global $db_url, $db_type, $active_db; 02906 $chado_exists = 0; 02907 02908 // only postgres can support search paths. So if this is MysQL then 02909 // just run the normal tripal_db_set_active function. 02910 if (strcmp($db_type, 'pgsql')==0) { 02911 02912 // if the 'chado' database is in the $db_url variable then chado is 02913 // not in the same Drupal database, so we don't need to set any 02914 // search_path and can just change the database 02915 if (is_array($db_url)) { 02916 if (isset($db_url[$dbname])) { 02917 return db_set_active($dbname); 02918 } 02919 } 02920 02921 // if this is the default database then set the search path and return 02922 if (strcmp($dbname, 'default')==0) { 02923 tripal_db_set_default_search_path(); 02924 return db_set_active($dbname); 02925 } 02926 // if the user requests a database other than the default 02927 // then we need to try and set the chado search path. This 02928 // only works if Chado is local to the Drpual database. If it 02929 // fails then do nothing. 02930 else { 02931 if (tripal_db_set_chado_search_path($dbname)) { 02932 // if the chado schema is local to Drupal then 02933 // just return the active database. 02934 return 'default'; 02935 } 02936 else { 02937 watchdog('tripal_core', "Cannot set 'search_path' variable for Postgres to %dbname", 02938 array('%dbname' => $dbname), WATCHDOG_ERROR); 02939 } 02940 } 02941 } 02942 // a non postgres database 02943 else { 02944 return db_set_active($dbname); 02945 } 02946 } 02947 02953 function tripal_db_get_search_path() { 02954 $path = db_fetch_object(db_query("show search_path")); 02955 return $path->search_path; 02956 } 02957 02966 function tripal_db_set_chado_search_path($dbname) { 02967 02968 // check to make sure the chado schema exists 02969 $chado_exists = variable_get('chado_schema_exists', FALSE); 02970 if (!$chado_exists) { 02971 $chado_exists = tripal_core_chado_schema_exists(); 02972 } 02973 02974 // here we make the assumption that the default database schema is 02975 // 'public'. This will most likely always be the case but if not, 02976 // then this code will break 02977 if ($chado_exists) { 02978 db_query('set search_path to %s', "$dbname,public"); 02979 return TRUE; 02980 } 02981 else { 02982 return FALSE; 02983 } 02984 } 02985 02994 function tripal_db_set_default_search_path() { 02995 // we make the assumption that the default schema is 'public'. 02996 $chado_exists = variable_get('chado_schema_exists', FALSE); 02997 if ($chado_exists) { 02998 db_query('set search_path to %s', 'public,chado'); 02999 } 03000 else { 03001 db_query('set search_path to %s', 'public'); 03002 } 03003 } 03013 function tripal_core_is_sql_prepared($statement_name) { 03014 global $prepared_statements; 03015 03016 if (!is_array($prepared_statements)) { 03017 watchdog('tripal_core', "tripal_core_is_sql_prepared: argument must be an array", array(), WATCHDOG_ERROR); 03018 return FALSE; 03019 } 03020 03021 // check to see if the statement is prepared already 03022 if (in_array($statement_name, $prepared_statements)) { 03023 return TRUE; 03024 } 03025 03026 // @coder-ignore: acting on postgres tables rather then drupal schema therefore, table prefixing does not apply 03027 $sql = "SELECT name FROM pg_prepared_statements WHERE name = '%s'"; 03028 // do not use 'chado_query' here or it causes memory-leaks 03029 $result = db_fetch_object(db_query($sql, $statement_name)); 03030 03031 if ($result) { 03032 return TRUE; 03033 } 03034 return FALSE; 03035 } 03036 03050 function tripal_core_chado_prepare($statement_name, $psql, $args) { 03051 global $persistent_chado; 03052 global $prepared_statements; 03053 03054 if (!$persistent_chado) { 03055 watchdog('tripal_core', "chado_prepare: not able to prepare '%name' statement as no persistent connection is available", array('%name' => $statement_name, '%sql' => $psql), WATCHDOG_ERROR); 03056 return FALSE; 03057 } 03058 03059 // Check to see if this statement was already prepared 03060 if (tripal_core_is_sql_prepared($statement_name)) { 03061 // check that the arguments are the same 03062 $prepared_args = $prepared_statements[$statement_name]['prepared_args']; 03063 $prepared_sql = $prepared_statements[$statement_name]['prepared_sql']; 03064 if ($prepared_args == $args) { 03065 // This statement is already prepared 03066 return TRUE; 03067 } 03068 else { 03069 // Although a statement with this name is already prepared it is not the same! 03070 watchdog('tripal_core', "chado_prepare: '%name' statement already prepared with different arguments! ". 03071 "You want to prepare \n%sql\n with \n%values\n and the existing statement is \n%esql\n with \n%existing", 03072 array('%name' => $statement_name, '%sql' => $psql, '%values' => print_r($args, TRUE), '%esql' => $prepared_sql, 03073 '%existing' => print_r($prepared_args, TRUE)), WATCHDOG_ERROR); 03074 return FALSE; 03075 } 03076 } 03077 03078 $status = chado_query($psql); 03079 if (!$status) { 03080 watchdog('tripal_core', "chado_prepare: not able to prepare '%name' statement for: %sql", array('%name' => $statement_name, '%sql' => $psql), WATCHDOG_ERROR); 03081 return FALSE; 03082 } 03083 else { 03084 $prepared_statements[$statement_name] = array(); 03085 $prepared_statements[$statement_name]['prepared_args'] = $args; 03086 $prepared_statements[$statement_name]['prepared_sql'] = $psql; 03087 return TRUE; 03088 } 03089 } 03090 03102 function tripal_core_chado_execute_prepared($statement_name, $sql, $values) { 03103 global $prepared_statements; 03104 03105 if (!tripal_core_is_sql_prepared($statement_name)) { 03106 watchdog('tripal_core', "tripal_core_chado_execute_prepared: Cannot execute an unprepared statement: '%name'", array('%name' => $statement_name), WATCHDOG_ERROR); 03107 return FALSE; 03108 } 03109 03110 // Before Executing, Ensure that all the values are supplied 03111 $required_values = $prepared_statements[$statement_name]['prepared_args']; 03112 if (!$required_values) { 03113 watchdog('tripal_core', "tripal_core_chado_execute_prepared: missing prepare arguments for this statement: '%name'", array('%name' => $statement_name), WATCHDOG_ERROR); 03114 return FALSE; 03115 } 03116 03117 if (sizeof($required_values) == sizeof($values)) { 03118 03119 $error = FALSE; 03120 foreach ($values as $k => $v) { 03121 if (isset($required_values[$k])) { 03122 switch ($required_values[$k]) { 03123 case 'text': 03124 $check = is_string($v); 03125 if ($v != '' and !$check) { 03126 watchdog('tripal_core', "chado_execute_prepared: wrong argument type supplied for '%name' statement, field %k. Expected %required but recieved '%value'", 03127 array('%name' => $statement_name, '%k' => $k+1, '%required' => $required_values[$k], '%value' => print_r($v, TRUE)), WATCHDOG_ERROR); 03128 return FALSE; 03129 } 03130 break; 03131 case 'int': 03132 $check = is_numeric($v); 03133 if (!$check) { 03134 watchdog('tripal_core', "chado_execute_prepared: wrong argument type supplied for '%name' statement, field %k. Expected %required but recieved '%value'", 03135 array('%name' => $statement_name, '%k' => $k+1, '%required' => $required_values[$k], '%value' => print_r($v, TRUE)), WATCHDOG_ERROR); 03136 return FALSE; 03137 } 03138 break; 03139 case 'bool': 03140 if ($v != 'TRUE' and $v != 'FALSE') { 03141 watchdog('tripal_core', "chado_execute_prepared: wrong argument type supplied for '%name' statement, field %k. Expected %required but recieved '%value'", 03142 array('%name' => $statement_name, '%k' => $k+1, '%required' => $required_values[$k], '%value' => print_r($v, TRUE)), WATCHDOG_ERROR); 03143 return FALSE; 03144 } 03145 break; 03146 case 'numeric': 03147 $check = is_numeric($v); 03148 if (!$check) { 03149 watchdog('tripal_core', "chado_execute_prepared: wrong argument type supplied for '%name' statement, field %k. Expected %required but recieved '%value'", 03150 array('%name' => $statement_name, '%k' => $k+1, '%required' => $required_values[$k], '%value' => print_r($v, TRUE)), WATCHDOG_ERROR); 03151 return FALSE; 03152 } 03153 break; 03154 default: 03155 watchdog('tripal_core', "chado_execute_prepared: unsupported argument type (supplied for '%name' statement %type)", 03156 03157 array('%name' => $statement_name, '%type' => $required_values[$k]), WATCHDOG_WARNING); 03158 break; 03159 } 03160 } 03161 else { 03162 watchdog('tripal_core', "chado_execute_prepared: wrong number of arguments supplied for '%name' statement. Expected %required but recieved %values", 03163 array('%name' => $statement_name, '%required' => print_r($required_values, TRUE), '%values' => print_r($values, TRUE)), WATCHDOG_ERROR); 03164 return FALSE; 03165 } 03166 } 03167 03168 // Since all values are supplied, execute 03169 $resource = chado_query($sql, $values); 03170 return $resource; 03171 } 03172 else { 03173 watchdog('tripal_core', "chado_execute_prepared: wrong number of arguments supplied for '%name' statement. ' . 03174 'Expected %required but recieved %values. Statement: %statement.", 03175 array('%name' => $statement_name, '%required' => print_r($required_values, TRUE), 03176 '%values' => print_r($values, TRUE), '%statement' => $prepared_statements[$statement_name]['prepared_sql']), WATCHDOG_ERROR); 03177 return FALSE; 03178 } 03179 } 03180 03187 function tripal_core_chado_clear_prepared ($statement_name_regex = NULL) { 03188 global $prepared_statements; 03189 03190 if ($statement_name_regex) { 03191 $resource = chado_query("SELECT * FROM pg_catalog.pg_prepared_statements WHERE name~'%s'",$statement_name_regex); 03192 while ($r = db_fetch_object($resource)) { 03193 $k = array_search($r->name, $prepared_statements); 03194 unset($prepared_statements[$k]); 03195 chado_query('DEALLOCATE PREPARE %s',$r->name); 03196 } 03197 } 03198 else { 03199 $prepared_statements = array(); 03200 chado_query('DEALLOCATE PREPARE ALL'); 03201 } 03202 } 03203 03214 function tripal_db_persistent_chado() { 03215 global $db_url; 03216 global $persistent_chado; 03217 03218 // get connection if it already exists otherwise we need to set it 03219 if ($persistent_chado) { 03220 return $persistent_chado; 03221 } 03222 else { 03223 if (is_array($db_url) && isset($db_url['chado'])) { 03224 $connection = db_connect($db_url['chado']); 03225 if (!$connection) { 03226 watchdog('tripal_core', "Could not create persistant connection", array(), WATCHDOG_ERROR); 03227 return FALSE; 03228 } 03229 $persistent_chado = $connection; 03230 } 03231 else { 03232 if (is_array($db_url)) { 03233 $connection = db_connect($db_url['default']); 03234 } 03235 else { 03236 $connection = db_connect($db_url); 03237 } 03238 if (!$connection) { 03239 $persistent_chado = NULL; 03240 watchdog('tripal_core', "Could not create persistant connection", array(), WATCHDOG_ERROR); 03241 return FALSE; 03242 } 03243 $persistent_chado = $connection; 03244 } 03245 return $connection; 03246 } 03247 return FALSE; 03248 } 03249 03253 function tripal_db_release_persistent_chado() { 03254 $persistent_chado = NULL; 03255 } 03256 03260 function tripal_db_start_transaction() { 03261 $connection = tripal_db_persistent_chado(); 03262 if ($connection) { 03263 chado_query("BEGIN"); 03264 return $connection; 03265 } 03266 return FALSE; 03267 } 03268 03272 function tripal_db_set_savepoint_transaction($savepoint, $release = FALSE) { 03273 // Postgresql requires a savepoint of the same name to be unset before re-use 03274 if ($release) { 03275 chado_query("RELEASE SAVEPOINT %s", $savepoint); 03276 } 03277 chado_query("SAVEPOINT %s", $savepoint); 03278 } 03279 03285 function tripal_db_commit_transaction() { 03286 chado_query("COMMIT"); 03287 } 03288 03304 function tripal_db_rollback_transaction($savepoint = NULL, $commit = TRUE) { 03305 03306 if ($savepoint) { 03307 chado_query("ROLLBACK TO SAVEPOINT %s", $savepoint); 03308 } 03309 else { 03310 chado_query("ROLLBACK"); 03311 } 03312 03313 if ($commit) { 03314 tripal_db_commit_transaction(); 03315 } 03316 03317 } 03318 03342 function tripal_get_max_chado_rank($tablename, $where_options) { 03343 $where= array(); 03344 03345 //generate the where clause from supplied options 03346 // the key is the column name 03347 foreach ($where_options as $key => $val_array) { 03348 if (preg_match('/INT/', $val_array['type'])) { 03349 $where[] = $key . "=" . $val_array['value']; 03350 } 03351 else { 03352 if ($val_array['exact']) { 03353 $operator='='; 03354 } 03355 else { 03356 $operator='~'; 03357 } 03358 $where[] = $key . $operator . "'" . $val_array['value'] . "'"; 03359 } 03360 } 03361 $previous_db = tripal_db_set_active('chado'); 03362 $result = db_fetch_object(db_query("SELECT max(rank) as max_rank, count(rank) as count FROM %s WHERE %s", 03363 $tablename, implode(' AND ', $where))); 03364 tripal_db_set_active($previous_db); 03365 //drupal_set_message("Max Rank Query=SELECT max(rank) as max_rank, count(rank) as count FROM ".$tablename." WHERE ".implode(' AND ',$where)); 03366 if ($result->count > 0) { 03367 return $result->max_rank; 03368 } 03369 else { 03370 return -1; 03371 } 03372 } 03373 03386 function tripal_get_chado_custom_schema($table) { 03387 03388 $sql = "SELECT schema FROM {tripal_custom_tables} WHERE table_name = '%s'"; 03389 $custom = db_fetch_object(db_query($sql, $table)); 03390 if (!$custom) { 03391 return FALSE; 03392 } 03393 else { 03394 return unserialize($custom->schema); 03395 } 03396 } 03397 03404 function tripal_core_chado_schema_exists() { 03405 03406 $exists = variable_get('chado_schema_exists', FALSE); 03407 03408 if (!$exists) { 03409 // This is postgresql-specific code to check the existence of the chado schema 03410 // @coder-ignore: acting on pg_catalog schema rather then drupal schema therefore, table prefixing does not apply 03411 $sql = "SELECT nspname FROM pg_catalog.pg_namespace WHERE nspname = 'chado'"; 03412 if (db_fetch_object(db_query($sql))) { 03413 variable_set('chado_schema_exists', TRUE); 03414 return TRUE; 03415 } 03416 else { 03417 return FALSE; 03418 } 03419 } 03420 return TRUE; 03421 } 03433 function tripal_core_schema_exists($schema) { 03434 03435 // check that the chado schema now exists 03436 $sql = "SELECT nspname 03437 FROM pg_namespace 03438 WHERE has_schema_privilege(nspname, 'USAGE') and nspname = '%s' 03439 ORDER BY nspname"; 03440 $name = db_fetch_object(db_query($sql, $schema)); 03441 if (strcmp($name->nspname, $schema) != 0) { 03442 return FALSE; 03443 } 03444 03445 return TRUE; 03446 } 03447 03463 function tripal_core_get_chado_tables($include_custom = NULL) { 03464 03465 03466 // first get the chado version that is installed 03467 $v = tripal_core_get_chado_version(); 03468 03469 $tables = array(); 03470 if ($v == '1.2') { 03471 $tables_v1_2 = tripal_core_chado_get_v1_2_tables(); 03472 foreach ($tables_v1_2 as $table) { 03473 $tables[$table] = $table; 03474 } 03475 } 03476 if ($v == '1.11' or $v == '1.11 or older') { 03477 $tables_v1_11 = tripal_core_chado_get_v1_11_tables(); 03478 foreach ($tables_v1_11 as $table) { 03479 $tables[$table] = $table; 03480 } 03481 } 03482 03483 // now add in the custom tables too if requested 03484 if ($include_custom) { 03485 $sql = "SELECT table_name FROM {tripal_custom_tables}"; 03486 $resource = db_query($sql); 03487 03488 while ($r = db_fetch_object($resource)) { 03489 $tables[$r->table_name] = $r->table_name; 03490 } 03491 } 03492 03493 asort($tables); 03494 return $tables; 03495 } 03505 function tripal_core_set_chado_version() { 03506 global $db_url; 03507 03508 // check that Chado is installed if not return 'uninstalled as the version' 03509 $chado_exists = tripal_core_chado_schema_exists(); 03510 if (!$chado_exists) { 03511 // if it's not in the drupal database check to see if it's specified in the $db_url 03512 // in the settings.php 03513 if (!is_array($db_url) or !array_key_exists('chado', $db_url)) { 03514 // if it's not in the drupal database or specified in the $db_url then 03515 // return uninstalled as the version 03516 return 'not installed'; 03517 } 03518 } 03519 03520 // if the table doesn't exist then we don't know what version but we know 03521 // it must be 1.11 or older. 03522 $previous_db = tripal_db_set_active('chado'); 03523 $prop_exists = db_table_exists('chadoprop'); 03524 tripal_db_set_active($previous_db); 03525 if (!$prop_exists) { 03526 return "1.11 or older"; 03527 } 03528 03529 // we can't use the Tripal API to query this table 03530 // because the Tripal API depends on this function to 03531 // tell it the version. So, we need a typical SQL statement 03532 $sql = "SELECT value " 03533 ."FROM chadoprop CP " 03534 ." INNER JOIN cvterm CVT on CVT.cvterm_id = CP.type_id " 03535 ." INNER JOIN cv CV on CVT.cv_id = CV.cv_id " 03536 ."WHERE CV.name = 'chado_properties' and CVT.name = 'version'"; 03537 $previous_db = tripal_db_set_active('chado'); 03538 $v = db_fetch_object(db_query($sql)); 03539 tripal_db_set_active($previous_db); 03540 03541 // if we don't have a version in the chadoprop table then it must be 03542 // v1.11 or older 03543 if (!$v->value) { 03544 variable_set('chado_version', "1.11 or older"); 03545 return "1.11 or older"; 03546 } 03547 03548 variable_set('chado_version', $v->value); 03549 return $v->value; 03550 } 03571 function tripal_core_get_chado_version($exact = FALSE, $warn_if_unsupported = FALSE) { 03572 // first get the chado version that is installed 03573 $exact_version = variable_get('chado_version', ''); 03574 if (!$exact_version) { 03575 $exact_version = tripal_core_set_chado_version(); 03576 } 03577 03578 // Tripal only supports v1.11 or newer.. really this is the same as v1.1 03579 // but at the time the v1.11 schema API was written we didn't know that so 03580 // we'll return the version 1.11 so the schema API will work. 03581 if (strcmp($exact_version, '1.11 or older') == 0) { 03582 $exact_version = "1.11"; 03583 if ($warn_if_unsupported) { 03584 drupal_set_message(t("WARNING: Tripal does not fully support Chado version less than v1.11. If you are certain this is v1.11 03585 or if Chado was installed using an earlier version of Tripal then all is well. If not please upgrade to v1.11 or later"), 03586 'warning'); 03587 } 03588 } 03589 03590 // if not returing an exact version, return the version to the nearest 10th. 03591 // return 1.2 for all versions of 1.2x 03592 $effective_version = $exact_version; 03593 if (preg_match('/^1\.2\d+$/', $effective_version)) { 03594 $effective_version = "1.2"; 03595 } 03596 if ($warn_if_unsupported and ($effective_version != 1.11 and $effective_version != 1.2 and $effective_version != 'not installed')) { 03597 drupal_set_message(t("WARNING: The currently installed version of Chado, v$exact_version, is not fully compatible with Tripal."), 'warning'); 03598 } 03599 // if the callee has requested the exact version then return it 03600 if ($exact) { 03601 return $exact_version; 03602 } 03603 03604 return $effective_version; 03605 } 03618 function tripal_core_get_chado_table_schema($table) { 03619 03620 // first get the chado version that is installed 03621 $v = tripal_core_get_chado_version(); 03622 03623 // get the table array from the proper chado schema 03624 $v = preg_replace("/\./", "_", $v); // reformat version for hook name 03625 $table_arr = module_invoke_all("chado_schema_v" . $v . "_" . $table); 03626 03627 // if the table_arr is empty then maybe this is a custom table 03628 if (!is_array($table_arr) or count($table_arr) == 0) { 03629 $table_arr = tripal_get_chado_custom_schema($table); 03630 } 03631 03632 return $table_arr; 03633 } 03647 function tripal_core_clean_orphaned_nodes($table, $job_id) { 03648 $count = 0; 03649 03650 // build the SQL statments needed to check if nodes point to valid analyses 03651 $dsql = "SELECT * FROM {node} WHERE type = 'chado_%s' order by nid"; 03652 $nsql = "SELECT * FROM {node} WHERE nid = %d"; 03653 $csql = "SELECT * FROM {chado_%s} where nid = %d "; 03654 $clsql= "SELECT * FROM {chado_%s}"; 03655 $lsql = "SELECT * FROM %s where %s_id = %d "; 03656 03657 // load into nodes array 03658 print "Getting nodes\n"; 03659 $nodes = array(); 03660 $res = db_query($dsql, $table); 03661 while ($node = db_fetch_object($res)) { 03662 $nodes[$count] = $node; 03663 $count++; 03664 } 03665 03666 // load the chado_$table into an array 03667 print "Getting chado_$table\n"; 03668 $cnodes = array(); 03669 $res = db_query($clsql, $table); 03670 while ($node = db_fetch_object($res)) { 03671 $cnodes[$count] = $node; 03672 $count++; 03673 } 03674 $interval = intval($count * 0.01); 03675 if ($interval < 1) { 03676 $interval = 1; 03677 } 03678 03679 // iterate through all of the chado_$table entries and remove those 03680 // that don't have a node or don't have a $table record in chado.libary 03681 print "Verifying all chado_$table Entries\n"; 03682 $deleted = 0; 03683 foreach ($cnodes as $nid) { 03684 03685 // update the job status every 1% analyses 03686 if ($job_id and $i % $interval == 0) { 03687 tripal_job_set_progress($job_id, intval(($i / $count) * 100)); 03688 } 03689 03690 // see if the node exits, if not remove the entry from the chado_$table table 03691 $node = db_fetch_object(db_query($nsql, $nid->nid)); 03692 if (!$node) { 03693 $deleted++; 03694 db_query("DELETE FROM {chado_%s} WHERE nid = %d", $table, $nid->nid); 03695 $message = "chado_$table missing node.... DELETING: $nid->nid"; 03696 watchdog('tripal_core', $message, array(), WATCHDOG_WARNING); 03697 } 03698 03699 // see if the record in chado exist, if not remove the entry from the chado_$table 03700 $table_id = $table . "_id"; 03701 $record = db_fetch_object(chado_query($lsql, $table, $table, $nid->$table_id)); 03702 if (!$record) { 03703 $deleted++; 03704 db_query("DELETE FROM {chado_%s} WHERE %s_id = '%d'", $table, $table, $nid->$table_id); 03705 $message = "chado_$table missing $table.... DELETING entry."; 03706 watchdog('tripal_core', $message, array(), WATCHDOG_WARNING); 03707 } 03708 $i++; 03709 } 03710 print "\t$deleted chado_$table entries missing either a node or chado entry.\n"; 03711 03712 // iterate through all of the nodes and delete those that don't 03713 // have a corresponding entry in chado_$table 03714 $deleted = 0; 03715 foreach ($nodes as $node) { 03716 03717 // update the job status every 1% libraries 03718 if ($job_id and $i % $interval == 0) { 03719 tripal_job_set_progress($job_id, intval(($i / $count) * 100)); 03720 } 03721 03722 // check to see if the node has a corresponding entry 03723 // in the chado_$table table. If not then delete the node. 03724 $link = db_fetch_object(db_query($csql, $table, $node->nid)); 03725 if (!$link) { 03726 if (node_access('delete', $node)) { 03727 $deleted++; 03728 $message = "Node missing in chado_$table table.... DELETING node $node->nid"; 03729 watchdog("tripal_core", $message, array(), WATCHDOG_WARNING); 03730 node_delete($node->nid); 03731 } 03732 else { 03733 $message = "Node missing in chado_$table table.... but cannot delete due to improper permissions (node $node->nid)"; 03734 watchdog("tripal_core", $message, array(), WATCHDOG_WARNING); 03735 } 03736 } 03737 $i++; 03738 } 03739 print "\t$deleted nodes did not have corresponding chado_$table entries.\n"; 03740 03741 return ''; 03742 } 03743 03752 function tripal_core_is_chado_installed() { 03753 global $db_url, $db_type; 03754 03755 // first check if chado is in the db_url of the 03756 // settings.php file 03757 if (is_array($db_url)) { 03758 if (isset($db_url['chado'])) { 03759 return TRUE; 03760 } 03761 } 03762 03763 // check to make sure the chado schema exists 03764 return tripal_core_chado_schema_exists(); 03765 } 03766 03776 function tripal_core_is_chado_local() { 03777 global $db_url, $db_type; 03778 03779 $is_installed = tripal_core_is_chado_installed(); 03780 if ($is_installed) { 03781 if (is_array($db_url)) { 03782 if (isset($db_url['chado'])) { 03783 return FALSE; 03784 } 03785 } 03786 return TRUE; 03787 } 03788 return FALSE; 03789 } 03790 03799 function tripal_core_is_tripal_node_type($chado_table) { 03800 $linking_table = 'chado_' . $chado_table; 03801 if (db_table_exists($linking_table)) { 03802 return TRUE; 03803 } 03804 else { 03805 return FALSE; 03806 } 03807 }