Tripal v1.0 (6.x-1.0)
tripal_core_chado.api.inc
Go to the documentation of this file.
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 &gt;field_name&lt;  with the current field name &
01820     $criteria = preg_replace('/&gt;field_name&lt; /', addslashes($field_name), $criteria);
01821     // if field_value needed we can't deal with this field yet
01822     if (preg_match('/&gt;field_value&lt; /', $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 &gt;field_name&lt;  with the current field name &
01851       $criteria = preg_replace('/&gt;field_name&lt; /', 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('/&gt;field_value&lt; /', $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('/&gt;field_value&lt; /', $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('/&gt;field_value&lt; /', 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("&gt;field_value&lt; ") > 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 }
 All Classes Files Functions Variables