tripal_core.api.inc 90 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420
  1. <?php
  2. require_once "tripal_core.schema_v1.2.api.inc";
  3. require_once "tripal_core.schema_v1.11.api.inc";
  4. /**
  5. * @file
  6. * The Tripal Core API
  7. *
  8. * This file provides the API needed for all other Tripal and Tripal dependent
  9. * modules.
  10. *
  11. * @defgroup tripal_api Tripal API
  12. * @{
  13. * Provides an application programming interface (API) for Tripal
  14. *
  15. * The Tripal API currently provides generic insert/update/select functions for all chado content as
  16. * well as some module specific functions that insert/update/delete/select specific chado content.
  17. *
  18. * This API is currently in its infancy and some necessary functions might be missing. If you find
  19. * a missing function that you think should be included go to the sourceforge feature request
  20. * page and request it's inclusion in the API. Such feature requests with a working function
  21. * definition will be given priority.
  22. * @}
  23. *
  24. * @defgroup tripal_chado_api Core Module Chado API
  25. * @{
  26. * Provides an application programming interface (API) to manage data withing the Chado database.
  27. * This includes functions for selecting, inserting, updating and deleting records
  28. * in Chado tables. The functions will ensure proper integrity contraints are met
  29. * for inserts and updates.
  30. *
  31. * Also, a set of functions is provided for creating template variables. First,
  32. * is the tripal_core_generate_chado_vars which is used to select one ore more
  33. * records from a table and return an array with foreign key relationships fully
  34. * populated. For example, if selecting a feature, the organism_id and type_id
  35. * would be present in the returned array as a nested array with their respective
  36. * foreign keys also nested. The only fields that are not included are text
  37. * fields (which may be very large) or many-to-many foreign key relationships.
  38. * However, these fields and relationships can be expanded using the
  39. * tripal_core_expand_chado_vars.
  40. *
  41. * When a row from a chado table is selected using these two functions, it provides
  42. * a way for users who want to cutomize Drupal template files to access all data
  43. * associate with a specific record.
  44. *
  45. * Finally, the property tables in Chado generally follow the same format. Therefore
  46. * there is a set of functions for inserting, updating and deleting properties for
  47. * any table. This provides quick lookup of properties (provided the CV term is
  48. * known).
  49. *
  50. * @}
  51. * @ingroup tripal_api
  52. *
  53. * @defgroup tripal_files_api Core Module Files API
  54. * @{
  55. * Provides an application programming interface (API) for managing files within
  56. * the Tripal data directory structure.
  57. *
  58. * @}
  59. * @ingroup tripal_api
  60. /**
  61. * Provides a generic routine for inserting into any Chado table
  62. *
  63. * Use this function to insert a record into any Chado table. The first
  64. * argument specifies the table for inserting and the second is an array
  65. * of values to be inserted. The array is mutli-dimensional such that
  66. * foreign key lookup values can be specified.
  67. *
  68. * @param $table
  69. * The name of the chado table for inserting
  70. * @param $values
  71. * An associative array containing the values for inserting.
  72. * @param $options
  73. * An array of options such as:
  74. * - statement_name: the name of the prepared statement to use. If the statement
  75. * has not yet been prepared it will be prepared automatically. On subsequent
  76. * calls with the same statement_name only an execute on the previously
  77. * prepared statement will occur.
  78. * - is_prepared: TRUE or FALSE. Whether or not the statement is prepared. By
  79. * default if the statement is not prepared it will be automatically.
  80. * However to avoid this check, which requires a database query you can
  81. * set this value to true and the check will not be performed.
  82. * - skip_validation: TRUE or FALSE. If TRUE will skip all the validation steps and
  83. * just try to insert as is. This is much faster but results in unhandled
  84. * non user-friendly errors if the insert fails.
  85. *
  86. * @return
  87. * On success this function returns TRUE. On failure, it returns FALSE.
  88. *
  89. * Example usage:
  90. * @code
  91. * $values = array(
  92. * 'organism_id' => array(
  93. * 'genus' => 'Citrus',
  94. * 'species' => 'sinensis',
  95. * ),
  96. * 'name' => 'orange1.1g000034m.g',
  97. * 'uniquename' => 'orange1.1g000034m.g',
  98. * 'type_id' => array (
  99. * 'cv_id' => array (
  100. * 'name' => 'sequence',
  101. * ),
  102. * 'name' => 'gene',
  103. * 'is_obsolete' => 0
  104. * ),
  105. * );
  106. * $result = tripal_core_chado_insert('feature',$values);
  107. * @endcode
  108. * The above code inserts a record into the feature table. The $values array is
  109. * nested such that the organism is selected by way of the organism_id foreign
  110. * key constraint by specifying the genus and species. The cvterm is also
  111. * specified using its foreign key and the cv_id for the cvterm is nested as
  112. * well.
  113. *
  114. * @ingroup tripal_chado_api
  115. */
  116. function tripal_core_chado_insert($table, $values, $options = array()) {
  117. $insert_values = array();
  118. // we need to get a persistent connection. If one exists this function
  119. // will not recreate it, but if not it will create one and store it in
  120. // a Drupal variable for reuse later.
  121. tripal_db_persistent_chado();
  122. // Determine plan of action
  123. if ($options['statement_name']) {
  124. // we have a prepared statment (or want to create one) so set $prepared = TRUE
  125. $prepared = TRUE;
  126. }
  127. if (array_key_exists('skip_validation', $options)) {
  128. $validate = !$options['skip_validation'];
  129. }
  130. else {
  131. $validate = TRUE;
  132. }
  133. // get the table description
  134. $table_desc = tripal_core_get_chado_table_schema($table);
  135. if (empty($table_desc)) {
  136. watchdog('tripal_core', 'tripal_core_chado_insert: There is no table description for !table_name', array('!table_name' => $table), WATCHDOG_WARNING);
  137. }
  138. // iterate through the values array and create a new 'insert_values' array
  139. // that has all the values needed for insert with all foreign relationsihps
  140. // resolved.
  141. foreach ($values as $field => $value) {
  142. if (is_array($value)) {
  143. // select the value from the foreign key relationship for this value
  144. $results = tripal_core_chado_get_foreign_key($table_desc, $field, $value);
  145. if (sizeof($results) > 1) {
  146. 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);
  147. }
  148. elseif (sizeof($results) < 1) {
  149. //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);
  150. }
  151. else {
  152. $insert_values[$field] = $results[0];
  153. }
  154. }
  155. else {
  156. $insert_values[$field] = $value;
  157. }
  158. }
  159. if ($validate) {
  160. // check for violation of any unique constraints
  161. $ukeys = $table_desc['unique keys'];
  162. $ukselect_cols = array();
  163. $ukselect_vals = array();
  164. if ($ukeys) {
  165. foreach ($ukeys as $name => $fields) {
  166. foreach ($fields as $index => $field) {
  167. // build the arrays for performing a select that will check the contraint
  168. array_push($ukselect_cols, $field);
  169. $ukselect_vals[$field] = $insert_values[$field];
  170. }
  171. // now check the constraint
  172. if (tripal_core_chado_select($table, $ukselect_cols, $ukselect_vals)) {
  173. watchdog('tripal_core', "tripal_core_chado_insert: Cannot insert duplicate record into $table table: " . print_r($values, 1), array(), 'WATCHDOG_ERROR');
  174. return FALSE;
  175. }
  176. }
  177. }
  178. // if trying to insert a field that is the primary key, make sure it also is unique
  179. $pkey = $table_desc['primary key'][0];
  180. if ($insert_values[$pkey]) {
  181. if (tripal_core_chado_select($table, array($pkey), array($pkey => $insert_values[$pkey]))) {
  182. watchdog('tripal_core', "tripal_core_chado_insert: Cannot insert duplicate primary key into $table table: " . print_r($values, 1), array(), 'WATCHDOG_ERROR');
  183. return FALSE;
  184. }
  185. }
  186. // make sure required fields have a value
  187. if (!is_array($table_desc['fields'])) {
  188. $table_desc['fields'] = array();
  189. watchdog('tripal_core', "tripal_core_chado_insert: %table not defined in tripal schema api", array('%table' => $table), 'WATCHDOG WARNING');
  190. }
  191. foreach ($table_desc['fields'] as $field => $def) {
  192. // a field is considered missing if it cannot be NULL and there is no default
  193. // value for it or it is of type 'serial'
  194. if ($def['not NULL'] == 1 and !array_key_exists($field, $insert_values) and !isset($def['default']) and strcmp($def['type'], serial) != 0) {
  195. watchdog('tripal_core', "tripal_core_chado_insert: Field $table.$field cannot be NULL: " . print_r($values, 1), array(), 'WATCHDOG_ERROR');
  196. return FALSE;
  197. }
  198. }
  199. } //end of validation
  200. // Now build the insert SQL statement
  201. $ifields = array(); //contains the names of the fields
  202. $ivalues = array(); //contains the values of the fields
  203. $itypes = array(); // contains %d/%s placeholders for the sql query
  204. $iplaceholders = array(); // contains $1/$2 placeholders for the prepare query
  205. $idatatypes = array(); //contains the data type of the fields (int, text, etc.)
  206. $i = 1;
  207. foreach ($insert_values as $field => $value) {
  208. $ifields[] = $field;
  209. $ivalues[] = $value;
  210. $iplaceholders[] = '$' . $i;
  211. $i++;
  212. if (strcmp($value, '__NULL__')==0) {
  213. $itypes[] = "NULL";
  214. $idatatypes[] = "NULL";
  215. }
  216. elseif (strcasecmp($table_desc['fields'][$field]['type'], 'serial')==0 OR
  217. strcasecmp($table_desc['fields'][$field]['type'], 'int')==0 OR
  218. strcasecmp($table_desc['fields'][$field]['type'], 'integer')==0) {
  219. $itypes[] = "%d";
  220. $idatatypes[] = 'int';
  221. }
  222. elseif (strcasecmp($table_desc['fields'][$field]['type'], 'boolean')==0) {
  223. $itypes[] = "%s";
  224. $idatatypes[] = 'bool';
  225. }
  226. else {
  227. $itypes[] = "'%s'";
  228. $idatatypes[] = 'text';
  229. }
  230. }
  231. // create the SQL
  232. $sql = "INSERT INTO {$table} (" . implode(", ", $ifields) . ") VALUES (" . implode(", ", $itypes) . ")";
  233. // if this is a prepared statement then execute it
  234. if ($prepared) {
  235. // if this is the first time we've run this query
  236. // then we need to do the prepare, otherwise just execute
  237. if ($options['is_prepared'] != TRUE and
  238. !tripal_core_is_sql_prepared($options['statement_name'])) {
  239. // prepare the statement
  240. $psql = "PREPARE " . $options['statement_name'] . " (" . implode(', ', $idatatypes) . ") AS INSERT INTO {$table} (" . implode(", ", $ifields) . ") VALUES (" . implode(", ", $iplaceholders) . ")";
  241. $status = chado_query($psql);
  242. if (!$status) {
  243. watchdog('tripal_core', "tripal_core_chado_insert: not able to prepare '%name' statement for: %sql", array('%name' => $options['statement_name'], '%sql' => $sql), 'WATCHDOG ERROR');
  244. return FALSE;
  245. }
  246. }
  247. $sql = "EXECUTE " . $options['statement_name'] . "(" . implode(", ", $itypes) . ")";
  248. $result = chado_query($sql, $ivalues);
  249. }
  250. // if it's not a prepared statement then insert normally
  251. else {
  252. $previous_db = tripal_db_set_active('chado'); // use chado database
  253. $result = db_query($sql, $ivalues);
  254. tripal_db_set_active($previous_db); // now use drupal database
  255. }
  256. // if we have a result then add primary keys to return array
  257. if ($result) {
  258. $primary_key = array();
  259. if (!is_array($table_desc['primary key'])) {
  260. $table_desc['primary key'] = array();
  261. watchdog('tripal_core', "tripal_core_chado_insert: %table not defined in tripal schema api", array('%table' => $table), 'WATCHDOG WARNING');
  262. }
  263. foreach ($table_desc['primary key'] as $field) {
  264. $value = db_last_insert_id($table, $field);
  265. $values[$field] = $value;
  266. }
  267. return $values;
  268. }
  269. else {
  270. watchdog('tripal_core', "tripal_core_chado_insert: Cannot insert record into $table table: " . print_r($values, 1), array(), 'WATCHDOG_ERROR');
  271. return FALSE;
  272. }
  273. return FALSE;
  274. }
  275. /**
  276. * Provides a generic function for deleting a record(s) from any chado table
  277. *
  278. * Use this function to delete a record(s) in any Chado table. The first
  279. * argument specifies the table to delete from and the second is an array
  280. * of values to match for locating the record(s) to be deleted. The arrays
  281. * are mutli-dimensional such that foreign key lookup values can be specified.
  282. *
  283. * @param $table
  284. * The name of the chado table for inserting
  285. * @param $match
  286. * An associative array containing the values for locating a record to update.
  287. *
  288. * @return
  289. * On success this function returns TRUE. On failure, it returns FALSE.
  290. *
  291. * Example usage:
  292. * @code
  293. $umatch = array(
  294. 'organism_id' => array(
  295. 'genus' => 'Citrus',
  296. 'species' => 'sinensis',
  297. ),
  298. 'uniquename' => 'orange1.1g000034m.g7',
  299. 'type_id' => array (
  300. 'cv_id' => array (
  301. 'name' => 'sequence',
  302. ),
  303. 'name' => 'gene',
  304. 'is_obsolete' => 0
  305. ),
  306. );
  307. $uvalues = array(
  308. 'name' => 'orange1.1g000034m.g',
  309. 'type_id' => array (
  310. 'cv_id' => array (
  311. 'name' => 'sequence',
  312. ),
  313. 'name' => 'mRNA',
  314. 'is_obsolete' => 0
  315. ),
  316. );
  317. * $result = tripal_core_chado_update('feature',$umatch,$uvalues);
  318. * @endcode
  319. * The above code species that a feature with a given uniquename, organism_id,
  320. * and type_id (the unique constraint for the feature table) will be deleted.
  321. * The organism_id is specified as a nested array that uses the organism_id
  322. * foreign key constraint to lookup the specified values to find the exact
  323. * organism_id. The same nested struture is also used for specifying the
  324. * values to update. The function will find all records that match the
  325. * columns specified and delete them.
  326. *
  327. * @ingroup tripal_chado_api
  328. */
  329. function tripal_core_chado_delete($table, $match) {
  330. $delete_matches = array(); // contains the values for the where clause
  331. // get the table description
  332. $table_desc = tripal_core_get_chado_table_schema($table);
  333. // get the values needed for matching in the SQL statement
  334. foreach ($match as $field => $value) {
  335. if (is_array($value)) {
  336. // if the user has specified an array of values to delete rather than
  337. // FK relationships the keep those in our match
  338. if (array_values($value) === $value) {
  339. $delete_matches[$field] = $value;
  340. }
  341. else {
  342. $results = tripal_core_chado_get_foreign_key($table_desc, $field, $value);
  343. if (sizeof($results) > 1) {
  344. 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);
  345. }
  346. elseif (sizeof($results) < 1) {
  347. //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);
  348. }
  349. else {
  350. $delete_matches[$field] = $results[0];
  351. }
  352. }
  353. }
  354. else {
  355. $delete_matches[$field] = $value;
  356. }
  357. }
  358. // now build the SQL statement
  359. $sql = "DELETE FROM {$table} WHERE ";
  360. $dargs = array();
  361. foreach ($delete_matches as $field => $value) {
  362. if (count($value) > 1) {
  363. $sql .= "$field IN (" . db_placeholders($value, 'varchar') . ") AND ";
  364. foreach ($value as $v) {
  365. $dargs[] = $v;
  366. }
  367. }
  368. else {
  369. if (strcmp($value, '__NULL__') == 0) {
  370. $sql .= " $field = NULL AND ";
  371. }
  372. elseif (strcmp($fields[$field]['type'], 'serial') == 0 or
  373. strcmp($fields[$field]['type'], 'int') == 0) {
  374. $sql .= " $field = %d AND ";
  375. }
  376. else {
  377. $sql .= " $field = '%s' AND ";
  378. }
  379. array_push($dargs, $value);
  380. }
  381. }
  382. $sql = drupal_substr($sql, 0, -4); // get rid of the trailing 'AND'
  383. // finally perform the delete. If successful, return the updated record
  384. $previous_db = tripal_db_set_active('chado'); // use chado database
  385. $result = db_query($sql, $dargs);
  386. tripal_db_set_active($previous_db); // now use drupal database
  387. if ($result) {
  388. return TRUE;
  389. }
  390. else {
  391. watchdog('tripal_core', "Cannot delete record in $table table. Match:" . print_r($match, 1) . ". Values: ". print_r($values, 1), array(), 'WATCHDOG_ERROR');
  392. return FALSE;
  393. }
  394. return FALSE;
  395. }
  396. /**
  397. * Provides a generic routine for updating into any Chado table
  398. *
  399. * Use this function to update a record in any Chado table. The first
  400. * argument specifies the table for inserting, the second is an array
  401. * of values to matched for locating the record for updating, and the third
  402. * argument give the values to update. The arrays are mutli-dimensional such
  403. * that foreign key lookup values can be specified.
  404. *
  405. * @param $table
  406. * The name of the chado table for inserting
  407. * @param $match
  408. * An associative array containing the values for locating a record to update.
  409. * @param $values
  410. * An associative array containing the values for updating.
  411. *
  412. * @return
  413. * On success this function returns TRUE. On failure, it returns FALSE.
  414. *
  415. * Example usage:
  416. * @code
  417. $umatch = array(
  418. 'organism_id' => array(
  419. 'genus' => 'Citrus',
  420. 'species' => 'sinensis',
  421. ),
  422. 'uniquename' => 'orange1.1g000034m.g7',
  423. 'type_id' => array (
  424. 'cv_id' => array (
  425. 'name' => 'sequence',
  426. ),
  427. 'name' => 'gene',
  428. 'is_obsolete' => 0
  429. ),
  430. );
  431. $uvalues = array(
  432. 'name' => 'orange1.1g000034m.g',
  433. 'type_id' => array (
  434. 'cv_id' => array (
  435. 'name' => 'sequence',
  436. ),
  437. 'name' => 'mRNA',
  438. 'is_obsolete' => 0
  439. ),
  440. );
  441. * $result = tripal_core_chado_update('feature',$umatch,$uvalues);
  442. * @endcode
  443. * The above code species that a feature with a given uniquename, organism_id,
  444. * and type_id (the unique constraint for the feature table) will be updated.
  445. * The organism_id is specified as a nested array that uses the organism_id
  446. * foreign key constraint to lookup the specified values to find the exact
  447. * organism_id. The same nested struture is also used for specifying the
  448. * values to update. The function will find the record that matches the
  449. * columns specified and update the record with the avlues in the $uvalues array.
  450. *
  451. * @ingroup tripal_chado_api
  452. */
  453. function tripal_core_chado_update($table, $match, $values) {
  454. $update_values = array(); // contains the values to be updated
  455. $update_matches = array(); // contains the values for the where clause
  456. // get the table description
  457. $table_desc = tripal_core_get_chado_table_schema($table);
  458. // get the values needed for matching in the SQL statement
  459. foreach ($match as $field => $value) {
  460. if (is_array($value)) {
  461. $results = tripal_core_chado_get_foreign_key($table_desc, $field, $value);
  462. if (sizeof($results) > 1) {
  463. 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);
  464. }
  465. elseif (sizeof($results) < 1) {
  466. //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);
  467. }
  468. else {
  469. $update_matches[$field] = $results[0];
  470. }
  471. }
  472. else {
  473. $update_matches[$field] = $value;
  474. }
  475. }
  476. // get the values used for updating
  477. foreach ($values as $field => $value) {
  478. if (is_array($value)) {
  479. $results = tripal_core_chado_get_foreign_key($table_desc, $field, $value);
  480. if (sizeof($results) > 1) {
  481. 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);
  482. }
  483. elseif (sizeof($results) < 1) {
  484. //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);
  485. }
  486. else {
  487. $update_values[$field] = $results[0];
  488. }
  489. }
  490. else {
  491. $update_values[$field] = $value;
  492. }
  493. }
  494. // now build the SQL statement
  495. $sql = "UPDATE {$table} SET ";
  496. $fields = $table_desc['fields'];
  497. $uargs = array();
  498. foreach ($update_values as $field => $value) {
  499. if (strcmp($value, '__NULL__') == 0) {
  500. $sql .= " $field = NULL, ";
  501. }
  502. elseif (strcmp($fields[$field]['type'], 'serial')==0 or
  503. strcmp($fields[$field]['type'], 'int')==0) {
  504. $sql .= " $field = %d, ";
  505. }
  506. else {
  507. $sql .= " $field = '%s', ";
  508. }
  509. array_push($uargs, $value);
  510. }
  511. $sql = drupal_substr($sql, 0, -2); // get rid of the trailing comma & space
  512. $sql .= " WHERE ";
  513. foreach ($update_matches as $field => $value) {
  514. if (strcmp($value, '__NULL__')==0) {
  515. $sql .= " $field = NULL AND ";
  516. }
  517. elseif (strcmp($fields[$field]['type'], 'serial')==0 or
  518. strcmp($fields[$field]['type'], 'int')==0) {
  519. $sql .= " $field = %d AND ";
  520. }
  521. else {
  522. $sql .= " $field = '%s' AND ";
  523. }
  524. array_push($uargs, $value);
  525. }
  526. $sql = drupal_substr($sql, 0, -4); // get rid of the trailing 'AND'
  527. // finally perform the update. If successful, return the updated record
  528. $previous_db = tripal_db_set_active('chado'); // use chado database
  529. $result = db_query($sql, $uargs);
  530. tripal_db_set_active($previous_db); // now use drupal database
  531. if ($result) {
  532. return TRUE;
  533. }
  534. else {
  535. watchdog('tripal_core', "Cannot update record in $table table. Match:" . print_r($match, 1) . ". Values: ". print_r($values, 1), array(), 'WATCHDOG_ERROR');
  536. return FALSE;
  537. }
  538. return FALSE;
  539. }
  540. /**
  541. * Provides a generic routine for selecting data from a Chado table
  542. *
  543. * Use this function to perform a simple select from any Chado table.
  544. *
  545. * @param $table
  546. * The name of the chado table for inserting
  547. * @param $columns
  548. * An array of column names
  549. * @param $values
  550. * An associative array containing the values for filtering the results. In the
  551. * case where multiple values for the same time are to be selected an additional
  552. * entry for the field should appear for each value
  553. * @param $options
  554. * An associative array of additional options where the key is the option
  555. * and the value is the value of that option.
  556. *
  557. * Additional Options Include:
  558. * - has_record
  559. * Set this argument to 'TRUE' to have this function return a numeric
  560. * value for the number of recrods rather than the array of records. this
  561. * can be useful in 'if' statements to check the presence of particula records.
  562. * - return_sql
  563. * Set this to 'TRUE' to have this function return an array where the first
  564. * element is the sql that would have been run and the second is an array of
  565. * arguments.
  566. * - case_insensitive_columns
  567. * An array of columns to do a case insensitive search on.
  568. * - regex_columns
  569. * An array of columns where the value passed in should be treated as a regular expression
  570. * - order_by
  571. * An associative array containing the column names of the table as keys
  572. * and the type of sort (i.e. ASC, DESC) as the values. The results in the
  573. * query will be sorted by the key values in the direction listed by the value
  574. * - statement_name: the name of the prepared statement to use. If the statement
  575. * has not yet been prepared it will be prepared automatically. On subsequent
  576. * calls with the same statement_name only an execute on the previously
  577. * prepared statement will occur.
  578. * - is_prepared: TRUE or FALSE. Whether or not the statement is prepared. By
  579. * default if the statement is not prepared it will be automatically.
  580. * However to avoid this check, which requires a database query you can
  581. * set this value to true and the check will not be performed.
  582. *
  583. * @return
  584. * A database query result resource, FALSE if the query was not executed
  585. * correctly, or the number of records in the dataset if $has_record is set.
  586. *
  587. * Example usage:
  588. * @code
  589. * $columns = array('feature_id', 'name');
  590. * $values = array(
  591. * 'organism_id' => array(
  592. * 'genus' => 'Citrus',
  593. * 'species' => array('sinensis', 'clementina'),
  594. * ),
  595. * 'uniquename' => 'orange1.1g000034m.g',
  596. * 'type_id' => array (
  597. * 'cv_id' => array (
  598. * 'name' => 'sequence',
  599. * ),
  600. * 'name' => 'gene',
  601. * 'is_obsolete' => 0
  602. * ),
  603. * );
  604. * $options = array(
  605. * 'statement_name' => 'sel_feature_genus_species_cvname'
  606. * 'order_by' => array(
  607. * 'name' => 'ASC'
  608. * ),
  609. * );
  610. * $result = tripal_core_chado_select('feature',$columns,$values,$options);
  611. * @endcode
  612. * The above code selects a record from the feature table using the three fields
  613. * that uniquely identify a feature. The $columns array simply lists the columns
  614. * to select. The $values array is nested such that the organism is identified by
  615. * way of the organism_id foreign key constraint by specifying the genus and
  616. * species. The cvterm is also specified using its foreign key and the cv_id
  617. * for the cvterm is nested as well. In the example above, two different species
  618. * are allowed to match
  619. *
  620. * @ingroup tripal_chado_api
  621. */
  622. function tripal_core_chado_select($table, $columns, $values, $options = NULL) {
  623. // we need to get a persistent connection. If one exists this function
  624. // will not recreate it, but if not it will create one and store it in
  625. // a Drupal variable for reuse later.
  626. tripal_db_persistent_chado();
  627. // get the options for this query
  628. if (!is_array($options)) {
  629. $options = array();
  630. }
  631. if (!$options['case_insensitive_columns']) {
  632. $options['case_insensitive_columns'] = array();
  633. }
  634. if (!$options['regex_columns']) {
  635. $options['regex_columns'] = array();
  636. }
  637. if (!$options['order_by']) {
  638. $options['order_by'] = array();
  639. }
  640. // if this is a prepared statement check to see if it has already been prepared
  641. if ($options['statement_name']) {
  642. $prepared = TRUE;
  643. }
  644. // check that our columns and values arguments are proper arrays
  645. if (!is_array($columns)) {
  646. watchdog('tripal_core', 'the $columns argument for tripal_core_chado_select must be an array.');
  647. return FALSE;
  648. }
  649. if (!is_array($values)) {
  650. watchdog('tripal_core', 'the $values argument for tripal_core_chado_select must be an array.');
  651. return FALSE;
  652. }
  653. // get the table description
  654. $table_desc = tripal_core_get_chado_table_schema($table);
  655. $select = '';
  656. $from = '';
  657. $where = '';
  658. $args = array();
  659. foreach ($values as $field => $value) {
  660. $select[] = $field;
  661. if (is_array($value)) {
  662. // if the user has specified multiple values for matching then this we
  663. // want to catch that and save them in our $where array, otherwise
  664. // we'll descend for a foreign key relationship
  665. if (array_values($value) === $value) {
  666. $where[$field] = $value;
  667. }
  668. else {
  669. // select the value from the foreign key relationship for this value
  670. $foreign_options = array(
  671. 'regex_columns' => $options['regex_columns'],
  672. 'case_insensitive_columns' => $options['case_insensitive_columns']
  673. );
  674. if ($options['statement_name']) {
  675. // add the fk relationship info to the prepared statement name so that
  676. // we can prepare the selects run by the recrusive tripal_core_chado_get_foreign_key
  677. // function.
  678. $foreign_options['statement_name'] = $options['statement_name'] . "fk_" . $table . "_" . $field;
  679. }
  680. if ($options['prepare']) {
  681. $foreign_options['prepare'] = $options['prepare'];
  682. }
  683. $results = tripal_core_chado_get_foreign_key($table_desc, $field, $value, $foreign_options);
  684. if (!$results or count($results) ==0) {
  685. // foreign key records are required
  686. // thus if none matched then return FALSE and alert the admin through watchdog
  687. //watchdog('tripal_core',
  688. // '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',
  689. // array('!table' => $table,
  690. // '!columns' => '<pre>' . print_r($columns, TRUE) . '</pre>',
  691. // '!values' => '<pre>' . print_r($values, TRUE) . '</pre>',
  692. // '!field' => $field,
  693. // ),
  694. // WATCHDOG_WARNING);
  695. return array();
  696. }
  697. else {
  698. $where[$field] = $results;
  699. }
  700. }
  701. }
  702. else {
  703. //need to catch a 0 and make int if integer field
  704. if ($table_desc['fields'][$field]['type'] == 'int') {
  705. $where[$field][] = (int) $value;
  706. }
  707. else {
  708. $where[$field][] = $value;
  709. }
  710. }
  711. }
  712. // now build the SQL and prepared SQL statements. We may not use
  713. // the prepared statement if it wasn't requested in the options of if the
  714. // argument in a where statement has multiple values.
  715. if (empty($where)) {
  716. // sometimes want to select everything
  717. $sql = "SELECT " . implode(', ', $columns) . " ";
  718. $sql .= "FROM {$table} ";
  719. // we don't prepare a statement if there is no where clause
  720. $prepared = FALSE;
  721. }
  722. else {
  723. $sql = "SELECT " . implode(', ', $columns) . " ";
  724. $sql .= "FROM {$table} ";
  725. $sql .= "WHERE ";
  726. $psql = $sql; // prepared SQL statement;
  727. $i = 1;
  728. $pvalues = array();
  729. foreach ($where as $field => $value) {
  730. // if we have multiple values returned then we need an 'IN' statement
  731. // in our where statement
  732. if (count($value) > 1) {
  733. $sql .= "$field IN (" . db_placeholders($value, 'varchar') . ") AND ";
  734. foreach ($value as $v) {
  735. $args[] = $v;
  736. // we can't do a prepared statement with an 'IN' statement in a
  737. // where clause because we can't guarantee we'll always have the
  738. // same number of elements.
  739. $prepared = FALSE;
  740. }
  741. }
  742. // if we have a single value then we need an = in our where statement
  743. else {
  744. $operator = '=';
  745. if (in_array($field, $options['regex_columns'])) {
  746. $operator = '~*';
  747. }
  748. // get the types for the prepared statement. First check if the type
  749. // is an integer
  750. if (strcasecmp($table_desc['fields'][$field]['type'], 'serial')==0 OR
  751. strcasecmp($table_desc['fields'][$field]['type'], 'int')==0 OR
  752. strcasecmp($table_desc['fields'][$field]['type'], 'integer')==0) {
  753. $sql .= "$field $operator %d AND ";
  754. $psql .= "$field $operator \$" . $i . " AND ";
  755. $args[] = $value[0];
  756. // set the variables needed for the prepared statement
  757. $idatatypes[] = 'int';
  758. $pvalues[] = $value[0];
  759. }
  760. elseif (strcasecmp($table_desc['fields'][$field]['type'], 'boolean')==0) {
  761. $sql .= "$field $operator %s AND ";
  762. $psql .= "$field $operator \$" . $i . " AND ";
  763. $args[] = $value[0];
  764. // set the variables needed for the prepared statement
  765. $idatatypes[] = 'bool';
  766. $pvalues[] = $value[0];
  767. }
  768. // else the type is a text
  769. else {
  770. if (in_array($field, $options['case_insensitive_columns'])) {
  771. $sql .= "lower($field) $operator lower('%s') AND ";
  772. $psql .= "lower($field) $operator lower('\$" . $i . "') AND ";
  773. $args[] = $value;
  774. }
  775. else {
  776. $sql .= "$field $operator '%s' AND ";
  777. $psql .= "$field $operator \$" . $i . " AND ";
  778. $args[] = $value[0];
  779. }
  780. // set the variables needed for the prepared statement
  781. $idatatypes[] = 'text';
  782. $pvalues[] = "'" . $value[0] . "'";
  783. }
  784. }
  785. $i++;
  786. }
  787. $sql = drupal_substr($sql, 0, -4); // get rid of the trailing 'AND '
  788. $psql = drupal_substr($psql, 0, -4); // get rid of the trailing 'AND '
  789. // finally add any ordering of the results to the SQL statement
  790. if (count($options['order_by']) > 0) {
  791. $sql .= " ORDER BY ";
  792. $psql .= " ORDER BY ";
  793. foreach ($options['order_by'] as $field => $dir) {
  794. $sql .= "$field $dir, ";
  795. $psql .= "$field $dir, ";
  796. }
  797. $sql = drupal_substr($sql, 0, -2); // get rid of the trailing ', '
  798. $psql = drupal_substr($psql, 0, -2); // get rid of the trailing ', '
  799. }
  800. // finish constructing the prepared SQL statement
  801. $psql = "PREPARE " . $options['statement_name'] . " (" . implode(', ', $idatatypes) . ") AS " . $psql;
  802. } // end if(empty($where)){ } else {
  803. // if the caller has requested the SQL rather than the results...
  804. // which happens in the case of wanting to use the Drupal pager, then do so
  805. if ($options['return_sql']) {
  806. return array('sql' => $sql, 'args' => $args);
  807. }
  808. // prepare the statement
  809. if ($prepared) {
  810. // if this is the first time we've run this query
  811. // then we need to do the prepare, otherwise just execute
  812. if ($options['is_prepared'] != TRUE and
  813. !tripal_core_is_sql_prepared($options['statement_name'])) {
  814. $status = chado_query($psql);
  815. # print "$psql\n";
  816. if (!$status) {
  817. watchdog('tripal_core', "tripal_core_chado_select: not able to prepare '%name' statement for: %sql", array('%name' => $options['statement_name'], '%sql' => $sql), 'WATCHDOG ERROR');
  818. return FALSE;
  819. }
  820. }
  821. $sql = "EXECUTE " . $options['statement_name'] . "(" . implode(", ", $pvalues) . ")";
  822. # print "$sql\n";
  823. $resource = chado_query($sql, $ivalues);
  824. }
  825. else {
  826. $previous_db = tripal_db_set_active('chado'); // use chado database
  827. $resource = db_query($sql, $args);
  828. tripal_db_set_active($previous_db); // now use drupal database
  829. }
  830. // format results into an array
  831. $results = array();
  832. while ($r = db_fetch_object($resource)) {
  833. $results[] = $r;
  834. }
  835. if ($options['has_record']) {
  836. return count($results);
  837. }
  838. # print "$psql\n";
  839. # print "$sql\n";
  840. # print '$results = ' . print_r($results, 1);
  841. return $results;
  842. }
  843. /**
  844. * Gets the value of a foreign key relationship
  845. *
  846. * This function is used by tripal_core_chado_select, tripal_core_chado_insert,
  847. * and tripal_core_chado_update to iterate through the associate array of
  848. * values that gets passed to each of those routines. The values array
  849. * is nested where foreign key contraints are used to specify a value that. See
  850. * documentation for any of those functions for further information.
  851. *
  852. * @param $table_desc
  853. * A table description for the table with the foreign key relationship to be identified generated by
  854. * hook_chado_<table name>_schema()
  855. * @param $field
  856. * The field in the table that is the foreign key.
  857. * @param $values
  858. * An associative array containing the values
  859. * @param $options
  860. * An associative array of additional options where the key is the option
  861. * and the value is the value of that option. These options are passed on to tripal_core_chado_select.
  862. *
  863. * Additional Options Include:
  864. * - case_insensitive_columns
  865. * An array of columns to do a case insensitive search on.
  866. * - regex_columns
  867. * An array of columns where the value passed in should be treated as a regular expression
  868. *
  869. * @return
  870. * A string containg the results of the foreign key lookup, or FALSE if failed.
  871. *
  872. * Example usage:
  873. * @code
  874. *
  875. * $values = array(
  876. * 'genus' => 'Citrus',
  877. * 'species' => 'sinensis',
  878. * );
  879. * $value = tripal_core_chado_get_foreign_key('feature', 'organism_id',$values);
  880. *
  881. * @endcode
  882. * The above code selects a record from the feature table using the three fields
  883. * that uniquely identify a feature. The $columns array simply lists the columns
  884. * to select. The $values array is nested such that the organism is identified by
  885. * way of the organism_id foreign key constraint by specifying the genus and
  886. * species. The cvterm is also specified using its foreign key and the cv_id
  887. * for the cvterm is nested as well.
  888. *
  889. * @ingroup tripal_chado_api
  890. */
  891. function tripal_core_chado_get_foreign_key($table_desc, $field, $values, $options = NULL) {
  892. if (!is_array($options)) {
  893. $options = array();
  894. }
  895. if (!$options['case_insensitive_columns']) {
  896. $options['case_insensitive_columns'] = array();
  897. }
  898. if (!$options['regex_columns']) {
  899. $options['regex_columns'] = array();
  900. }
  901. // get the list of foreign keys for this table description and
  902. // iterate through those until we find the one we're looking for
  903. $fkeys = $table_desc['foreign keys'];
  904. if ($fkeys) {
  905. foreach ($fkeys as $name => $def) {
  906. if (is_array($def['table'])) {
  907. //foreign key was described 2X
  908. $message = "The foreign key " . $name . " was defined twice. Please check modules "
  909. ."to determine if hook_chado_schema_<version>_" . $table_desc['table'] . "() was "
  910. ."implemented and defined this foreign key when it wasn't supposed to. Modules "
  911. ."this hook was implemented in: " . implode(', ',
  912. module_implements("chado_" . $table_desc['table'] . "_schema")) . ".";
  913. watchdog('tripal_core', $message);
  914. drupal_set_message(check_plain($message), 'error');
  915. continue;
  916. }
  917. $table = $def['table'];
  918. $columns = $def['columns'];
  919. // iterate through the columns of the foreign key relationship
  920. foreach ($columns as $left => $right) {
  921. // does the left column in the relationship match our field?
  922. if (strcmp($field, $left) == 0) {
  923. // the column name of the foreign key matches the field we want
  924. // so this is the right relationship. Now we want to select
  925. $select_cols = array($right);
  926. $result = tripal_core_chado_select($table, $select_cols, $values, $options);
  927. $fields = array();
  928. if ($result and count($result) > 0) {
  929. foreach ($result as $obj) {
  930. $fields[] = $obj->$right;
  931. }
  932. return $fields;
  933. }
  934. }
  935. }
  936. }
  937. }
  938. else {
  939. // TODO: what do we do if we get to this point and we have a fk
  940. // relationship expected but we don't have any definition for one in the
  941. // table schema??
  942. $version = variable_get('chado_version', '');
  943. $message = t("There is no foreign key relationship defined for " . $field . ".
  944. To define a foreign key relationship, determine the table this foreign
  945. key referrs to (<foreign table>) and then implement
  946. hook_chado_chado_schema_v<version>_<foreign table>(). See
  947. tripal_feature_chado_v1_2_schema_feature for an example. Chado version: $version");
  948. watchdog('tripal_core', $message);
  949. drupal_set_message(check_plain($message), 'error');
  950. }
  951. return array();
  952. }
  953. /**
  954. * Generates an object containing the full details of a record(s) in chado.
  955. *
  956. * This differs from the objects returned by tripal_core_chado_select in so far as all foreign key
  957. * relationships have been followed meaning you have more complete details. Thus this function
  958. * should be used whenever you need a full variable and tripal_core_chado_select should be used if
  959. * you only case about a few columns.
  960. *
  961. * @param $table
  962. * The name of the base table to generate a variable for
  963. * @param $values
  964. * A select values array that selects the records you want from the base table
  965. * (this has the same form as tripal_core_chado_select)
  966. * @param $base_options
  967. * An array containing options for the base table. For example, an
  968. * option of 'order_by' may be used to sort results in the base table
  969. * if more than one are returned. The options must be compatible with
  970. * the options accepted by the tripal_core_chado_select() function.
  971. * @return
  972. * Either an object (if only one record was selected from the base table)
  973. * or an array of objects (if more than one record was selected from the base table).
  974. *
  975. * Example Usage:
  976. * @code
  977. $values = array(
  978. 'name' => 'Medtr4g030710'
  979. );
  980. $features = tripal_core_generate_chado_var('feature', $values);
  981. * @endcode
  982. * This will return an object if there is only one feature with the name Medtr4g030710 or it will
  983. * return an array of feature objects if more than one feature has that name.
  984. *
  985. * Note to Module Designers: Fields can be excluded by default from these objects by implementing
  986. * one of the following hooks:
  987. * - hook_exclude_field_from_tablename_by_default (where tablename is the name of the table):
  988. * This hook allows you to add fields to be excluded on a per table basis. Simply implement
  989. * this hook to return an array of fields to be excluded. For example:
  990. * @code
  991. mymodule_exclude_field_from_feature_by_default() {
  992. return array('residues' => TRUE);
  993. }
  994. * @endcode
  995. * will ensure that feature.residues is ecluded from a feature object by default.
  996. * - hook_exclude_type_by_default:
  997. * This hook allows you to exclude fields from all tables that are of a given postgresql field
  998. * type. Simply implement this hook to return an array of postgresql types mapped to criteria.
  999. * Then all fields of that type where the criteria supplied returns TRUE will be excluded from
  1000. * any table. Tokens available in criteria are &gt;field_value&lt; and &gt;field_name&lt; . For example:
  1001. * @code
  1002. mymodule_exclude_type_by_default() {
  1003. return array('text' => 'length(&gt;field_value&lt; ) > 50');
  1004. }
  1005. * @endcode
  1006. * will exclude all text fields with a length > 50. Thus if $feature.residues is longer than 50 * it will be excluded, otherwise it will be added.
  1007. *
  1008. * @ingroup tripal_chado_api
  1009. */
  1010. function tripal_core_generate_chado_var($table, $values, $base_options = array()) {
  1011. $all = new stdClass();
  1012. // get description for the current table----------------------------------------------------------
  1013. $table_desc = tripal_core_get_chado_table_schema($table);
  1014. $table_primary_key = $table_desc['primary key'][0];
  1015. $table_columns = array_keys($table_desc['fields']);
  1016. // Expandable fields without value needed for criteria--------------------------------------------
  1017. $all->expandable_fields = array();
  1018. if ($table_desc['referring_tables']) {
  1019. $all->expandable_tables = $table_desc['referring_tables'];
  1020. }
  1021. else {
  1022. $all->expandable_tables = array();
  1023. }
  1024. $all->expandable_nodes = array();
  1025. // Get fields to be removed by name.................................
  1026. $fields_to_remove = module_invoke_all('exclude_field_from_' . $table . '_by_default');
  1027. foreach ($fields_to_remove as $field_name => $criteria) {
  1028. //replace &gt;field_name&lt; with the current field name &
  1029. $criteria = preg_replace('/&gt;field_name&lt; /', $field_name, $criteria);
  1030. // if field_value needed we can't deal with this field yet
  1031. if (preg_match('/&gt;field_value&lt; /', $criteria)) {
  1032. break;
  1033. }
  1034. //if criteria then remove from query
  1035. // @coder-ignore: only module designers can populate $criteria -not security risk
  1036. $success = drupal_eval('<?php return ' . $criteria . '; ?>');
  1037. // watchdog('tripal_core',
  1038. // 'Evaluating criteria (%criteria) for field %field in tripal_core_generate_chado_var for %table evaluated to %success',
  1039. // array('%table' => $table, '%criteria'=>$criteria, '%field' => $field_name, '%success'=>$success),
  1040. // WATCHDOG_NOTICE
  1041. // );
  1042. if ($success) {
  1043. unset($table_columns[array_search($field_name, $table_columns)]);
  1044. unset($fields_to_remove[$field_name]);
  1045. $all->expandable_fields[] = $table . '.' . $field_name;
  1046. }
  1047. }
  1048. //Get fields to be removed by type................................
  1049. $types_to_remove = module_invoke_all('exclude_type_by_default');
  1050. $field_types = array();
  1051. foreach ($table_desc['fields'] as $field_name => $field_array) {
  1052. $field_types[$field_array['type']][] = $field_name;
  1053. }
  1054. foreach ($types_to_remove as $field_type => $criteria) {
  1055. // if there are fields of that type to remove
  1056. if (is_array($field_types[$field_type])) {
  1057. //replace &gt;field_name&lt; with the current field name &
  1058. $criteria = preg_replace('/&gt;field_name&lt; /', $field_name, $criteria);
  1059. foreach ($field_types[$field_type] as $field_name) {
  1060. // if field_value needed we can't deal with this field yet
  1061. if (preg_match('/&gt;field_value&lt; /', $criteria)) {
  1062. $fields_to_remove[$field_name] = $criteria;
  1063. continue;
  1064. }
  1065. // if field_value needed we can't deal with this field yet
  1066. if (preg_match('/&gt;field_value&lt; /', $criteria)) {
  1067. break;
  1068. }
  1069. //if criteria then remove from query
  1070. // @coder-ignore: only module designers can populate $criteria -not security risk
  1071. $success = drupal_eval('<?php return ' . $criteria . '; ?>');
  1072. // watchdog('tripal_core',
  1073. // 'Evaluating criteria (%criteria) for field %field of $type in tripal_core_generate_chado_var for %table evaluated to %success',
  1074. // array('%table'=>$table, '%criteria'=>$criteria, '%field'=>$field_name, '%type'=>$field_type, '%success'=>$success),
  1075. // WATCHDOG_NOTICE
  1076. // );
  1077. if ($success) {
  1078. unset($table_columns[array_search($field_name, $table_columns)]);
  1079. $all->expandable_fields[] = $table . '.' . $field_name;
  1080. }
  1081. } //end of foreach field of that type
  1082. }
  1083. } //end of foreach type to be removed
  1084. // get the values for the record in the current table---------------------------------------------
  1085. $results = tripal_core_chado_select($table, $table_columns, $values, $base_options);
  1086. if ($results) {
  1087. foreach ($results as $key => $object) {
  1088. // Add empty expandable_x arrays
  1089. $object->expandable_fields = $all->expandable_fields;
  1090. $object->expandable_tables = $all->expandable_tables;
  1091. $object->expandable_nodes = $all->expandable_nodes;
  1092. // add curent table
  1093. $object->tablename = $table;
  1094. // check if the current table maps to a node type-----------------------------------------------
  1095. // if this table is connected to a node there will be a chado_tablename table in drupal
  1096. if (db_table_exists('chado_' . $table)) {
  1097. // that has a foreign key to this one ($table_desc['primary key'][0]
  1098. // and to the node table (nid)
  1099. $sql = "SELECT %s, nid FROM {chado_%s} WHERE %s=%d";
  1100. $mapping = db_fetch_object(db_query(
  1101. $sql,
  1102. $table_primary_key,
  1103. $table,
  1104. $table_primary_key,
  1105. $object->{$table_primary_key}
  1106. ));
  1107. if ($mapping->{$table_primary_key}) {
  1108. $object->nid = $mapping->nid;
  1109. $object->expandable_nodes[] = $table;
  1110. }
  1111. }
  1112. // remove any fields where criteria need to be evalulated---------------------------------------
  1113. foreach ($fields_to_remove as $field_name => $criteria) {
  1114. if (!isset($object->{$field_name})) {
  1115. break;
  1116. }
  1117. $criteria = preg_replace('/&gt;field_value&lt; /', $object->{$field_name}, $criteria);
  1118. //if criteria then remove from query
  1119. // @coder-ignore: only module designers can populate $criteria -not security risk
  1120. $success = drupal_eval('<?php return ' . $criteria . '; ?>');
  1121. // watchdog('tripal_core',
  1122. // 'Evaluating criteria (%criteria) for field %field in tripal_core_generate_chado_var for %table evaluated to %success',
  1123. // array('%table' => $table, '%criteria'=>$criteria, '%field' => $field_name, '%success'=>$success),
  1124. // WATCHDOG_NOTICE
  1125. // );
  1126. if ($success) {
  1127. unset($object->{$field_name});
  1128. $object->expandable_fields[] = $table . '.' . $field_name;
  1129. }
  1130. }
  1131. // recursively follow foreign key relationships nesting objects as we go------------------------
  1132. if ($table_desc['foreign keys']) {
  1133. foreach ($table_desc['foreign keys'] as $foreign_key_array) {
  1134. $foreign_table = $foreign_key_array['table'];
  1135. foreach ($foreign_key_array['columns'] as $foreign_key => $primary_key) {
  1136. // Note: Foreign key is the field in the current table whereas primary_key is the field in
  1137. // the table referenced by the foreign key
  1138. //Dont do anything if the foreign key is empty
  1139. if (empty($object->{$foreign_key})) {
  1140. break;
  1141. }
  1142. // get the record from the foreign table
  1143. $foreign_values = array($primary_key => $object->{$foreign_key});
  1144. $foreign_object = tripal_core_generate_chado_var($foreign_table, $foreign_values);
  1145. // add the foreign record to the current object in a nested manner
  1146. $object->{$foreign_key} = $foreign_object;
  1147. // Flatten expandable_x arrays so only in the bottom object
  1148. if (is_array($object->{$foreign_key}->expandable_fields)) {
  1149. $object->expandable_fields = array_merge(
  1150. $object->expandable_fields,
  1151. $object->{$foreign_key}->expandable_fields
  1152. );
  1153. unset($object->{$foreign_key}->expandable_fields);
  1154. }
  1155. if (is_array($object->{$foreign_key}->expandable_tables)) {
  1156. $object->expandable_tables = array_merge(
  1157. $object->expandable_tables,
  1158. $object->{$foreign_key}->expandable_tables
  1159. );
  1160. unset($object->{$foreign_key}->expandable_tables);
  1161. }
  1162. if (is_array($object->{$foreign_key}->expandable_nodes)) {
  1163. $object->expandable_nodes = array_merge(
  1164. $object->expandable_nodes,
  1165. $object->{$foreign_key}->expandable_nodes
  1166. );
  1167. unset($object->{$foreign_key}->expandable_nodes);
  1168. }
  1169. }
  1170. }
  1171. $results[$key] = $object;
  1172. }
  1173. }
  1174. }
  1175. // check only one result returned
  1176. if (sizeof($results) == 1) {
  1177. // add results to object
  1178. return $results[0];
  1179. }
  1180. elseif (!empty($results)) {
  1181. return $results;
  1182. }
  1183. else {
  1184. // no results returned
  1185. }
  1186. }
  1187. /**
  1188. * Retrieves fields/tables/nodes that were excluded by default from a variable and adds them
  1189. *
  1190. * This function exists to allow tripal_core_generate_chado_var() to excldue some
  1191. * fields/tables/nodes from the default form of a variable without making it extremely difficult for
  1192. * the tripal admin to get at these variables if he/she wants them.
  1193. *
  1194. * @param $object
  1195. * This must be an object generated using tripal_core_generate_chado_var()
  1196. * @param $type
  1197. * Must be one of 'field', 'table', 'node'. Indicates what is being expanded.
  1198. * @param $to_expand
  1199. * The name of the field/table/node to be expanded
  1200. * @param $table_options
  1201. * An array containing options for the base table. For example, an
  1202. * option of 'order_by' may be used to sort results in the base table
  1203. * if more than one are returned. The options must be compatible with
  1204. * the options accepted by the tripal_core_chado_select() function.
  1205. * @return
  1206. * A chado object supplemented with the field/table/node requested to be expanded
  1207. *
  1208. * Example Usage:
  1209. * @code
  1210. // Get a chado object to be expanded
  1211. $values = array(
  1212. 'name' => 'Medtr4g030710'
  1213. );
  1214. $features = tripal_core_generate_chado_var('feature', $values);
  1215. // Expand the organism node
  1216. $feature = tripal_core_expand_chado_vars($feature, 'node', 'organism');
  1217. // Expand the feature.residues field
  1218. $feature = tripal_core_expand_chado_vars($feature, 'field', 'feature.residues');
  1219. // Expand the feature properties (featureprop table)
  1220. $feature = tripal_core_expand_chado_vars($feature, 'table', 'featureprop');
  1221. * @endcode
  1222. *
  1223. * @ingroup tripal_chado_api
  1224. */
  1225. function tripal_core_expand_chado_vars($object, $type, $to_expand, $table_options = array()) {
  1226. $base_table = $object->tablename;
  1227. // check to see if they are expanding an array of objects
  1228. if (is_array($object)) {
  1229. foreach ($object as $index => $o) {
  1230. $object[$index] = tripal_core_expand_chado_vars($o, $type, $to_expand);
  1231. }
  1232. return $object;
  1233. }
  1234. switch ($type) {
  1235. case "field": //--------------------------------------------------------------------------------
  1236. if (preg_match('/(\w+)\.(\w+)/', $to_expand, $matches)) {
  1237. $tablename = $matches[1];
  1238. $fieldname = $matches[2];
  1239. $table_desc = tripal_core_get_chado_table_schema($tablename);
  1240. $values = array();
  1241. foreach ($table_desc['primary key'] as $key) {
  1242. $values[$key] = $object->{$key};
  1243. }
  1244. if ($base_table == $tablename) {
  1245. //get the field
  1246. $results = tripal_core_chado_select(
  1247. $tablename,
  1248. array($fieldname),
  1249. $values
  1250. );
  1251. $object->{$fieldname} = $results[0]->{$fieldname};
  1252. $object->expanded = $to_expand;
  1253. }
  1254. else {
  1255. //We need to recurse -the field is in a nested object
  1256. foreach ((array) $object as $field_name => $field_value) {
  1257. if (is_object($field_value)) {
  1258. $object->{$field_name} = tripal_core_expand_chado_vars(
  1259. $field_value,
  1260. 'field',
  1261. $to_expand
  1262. );
  1263. }
  1264. } //end of for each field in the current object
  1265. }
  1266. }
  1267. else {
  1268. watchdog(
  1269. 'tripal_core',
  1270. 'tripal_core_expand_chado_vars: Field (%field) not in the right format. It should be <tablename>.<fieldname>',
  1271. WATCHDOG_ERROR
  1272. );
  1273. }
  1274. break;
  1275. case "table": //--------------------------------------------------------------------------------
  1276. $foreign_table = $to_expand;
  1277. $foreign_table_desc = tripal_core_get_chado_table_schema($foreign_table);
  1278. // If it's connected to the base table
  1279. if ($foreign_table_desc['foreign keys'][$base_table]) {
  1280. foreach ($foreign_table_desc['foreign keys'][$base_table]['columns'] as $left => $right) {
  1281. if (!$object->{$right}) {
  1282. break;
  1283. }
  1284. if (is_array($values)) {
  1285. $values = array_merge($values, array($left => $object->{$right}) );
  1286. }
  1287. else {
  1288. $values = array($left => $object->{$right});
  1289. }
  1290. $foreign_object = tripal_core_generate_chado_var(
  1291. $foreign_table,
  1292. array($left => $object->{$right}),
  1293. $table_options
  1294. );
  1295. if ($foreign_object) {
  1296. // in the case where the foreign key relationships exists more
  1297. // than once with the same table we want to alter the
  1298. // array structure
  1299. if (count($foreign_table_desc['foreign keys'][$base_table]['columns']) > 1) {
  1300. if (!is_object($object->{$foreign_table})) {
  1301. $object->{$foreign_table} = new stdClass();
  1302. }
  1303. $object->{$foreign_table}->{$left} = $foreign_object;
  1304. $object->expanded = $to_expand;
  1305. }
  1306. else {
  1307. $object->{$foreign_table} = $foreign_object;
  1308. $object->expanded = $to_expand;
  1309. }
  1310. }
  1311. }
  1312. }
  1313. else {
  1314. //We need to recurse -the table has a relationship to one of the nested objects
  1315. foreach ((array) $object as $field_name => $field_value) {
  1316. // if we have a nested object ->expand the table in it
  1317. if (is_object($field_value)) {
  1318. $object->{$field_name} = tripal_core_expand_chado_vars(
  1319. $field_value,
  1320. 'table',
  1321. $foreign_table
  1322. );
  1323. }
  1324. }
  1325. }
  1326. break;
  1327. case "node": //---------------------------------------------------------------------------------
  1328. //if the node to be expanded is for our base table, then just expand it
  1329. if ($object->tablename == $to_expand) {
  1330. $node = node_load($object->nid);
  1331. if ($node) {
  1332. $object->expanded = $to_expand;
  1333. $node->expandable_fields = $object->expandable_fields;
  1334. unset($object->expandable_fields);
  1335. $node->expandable_tables = $object->expandable_tables;
  1336. unset($object->expandable_tables);
  1337. $node->expandable_nodes = $object->expandable_nodes;
  1338. unset($object->expandable_nodes);
  1339. $node->{$base_table} = $object;
  1340. $object = $node;
  1341. }
  1342. else {
  1343. watchdog(
  1344. 'tripal_core',
  1345. 'tripal_core_expand_chado_vars: No node matches the nid (%nid) supplied.',
  1346. array('%nid' => $object->nid),
  1347. WATCHDOG_ERROR
  1348. );
  1349. } //end of if node
  1350. }
  1351. else {
  1352. //We need to recurse -the node to expand is one of the nested objects
  1353. foreach ((array) $object as $field_name => $field_value) {
  1354. if (is_object($field_value)) {
  1355. $object->{$field_name} = tripal_core_expand_chado_vars(
  1356. $field_value,
  1357. 'node',
  1358. $to_expand
  1359. );
  1360. }
  1361. } //end of for each field in the current object
  1362. }
  1363. break;
  1364. default:
  1365. watchdog('tripal_core',
  1366. 'tripal_core_expand_chado_vars: Unrecognized type (%type). Should be one of "field", "table", "node".',
  1367. array('%type' => $type),
  1368. WATCHDOG_ERROR
  1369. );
  1370. return FALSE;
  1371. }
  1372. //move extended array downwards-------------------------------------------------------------------
  1373. if (!$object->expanded) {
  1374. //if there's no extended field then go hunting for it
  1375. foreach ( (array)$object as $field_name => $field_value) {
  1376. if (is_object($field_value)) {
  1377. if (isset($field_value->expanded)) {
  1378. $object->expanded = $field_value->expanded;
  1379. unset($field_value->expanded);
  1380. }
  1381. }
  1382. }
  1383. }
  1384. //try again becasue now we might have moved it down
  1385. if ($object->expanded) {
  1386. $expandable_name = 'expandable_' . $type . 's';
  1387. if ($object->{$expandable_name}) {
  1388. $key_to_remove = array_search($object->expanded, $object->{$expandable_name});
  1389. unset($object->{$expandable_name}[$key_to_remove]);
  1390. unset($object->expanded);
  1391. }
  1392. else {
  1393. // if there is an expandable array then we've reached the base object
  1394. // if we get here and don't have anything expanded then something went wrong
  1395. // watchdog(
  1396. // 'tripal_core',
  1397. // 'tripal_core_expand_chado_vars: Unable to expand the %type %to_expand',
  1398. // array('%type'=>$type, '%to_expand'=>$to_expand),
  1399. // WATCHDOG_ERROR
  1400. // );
  1401. } //end of it we've reached the base object
  1402. }
  1403. return $object;
  1404. }
  1405. /**
  1406. * Implements hook_exclude_type_by_default()
  1407. *
  1408. * This hooks allows fields of a specified type that match a specified criteria to be excluded by
  1409. * default from any table when tripal_core_generate_chado_var() is called. Keep in mind that if
  1410. * fields are excluded by default they can always be expanded at a later date using
  1411. * tripal_core_expand_chado_vars().
  1412. *
  1413. * Criteria are php strings that evaluate to either TRUE or FALSE. These strings are evaluated using
  1414. * drupal_eval() which suppresses syntax errors and throws watchdog entries of type php. There are
  1415. * also watchdog entries of type tripal_core stating the exact criteria evaluated. Criteria can
  1416. * contain the following tokens:
  1417. * - &gt;field_name&lt;
  1418. * Replaced by the name of the field to be excluded
  1419. * - &gt;field_value&lt;
  1420. * Replaced by the value of the field in the current record
  1421. * Also keep in mind that if your criteria doesn't contain the &gt;field_value&lt; token then it will be
  1422. * evaluated before the query is executed and if the field is excluded it won't be included in the
  1423. * query.
  1424. *
  1425. * @return
  1426. * An array of type => criteria where the type is excluded if the criteria evaluates to TRUE
  1427. *
  1428. * @ingroup tripal_chado_api
  1429. */
  1430. function tripal_core_exclude_type_by_default() {
  1431. return array('text' => "strlen('&gt;field_value&lt; ') > 100");
  1432. }
  1433. /**
  1434. * Implements hook_exclude_field_from_<tablename>_by_default()
  1435. *
  1436. * This hooks allows fields from a specified table that match a specified criteria to be excluded by
  1437. * default from any table when tripal_core_generate_chado_var() is called. Keep in mind that if
  1438. * fields are excluded by default they can always be expanded at a later date using
  1439. * tripal_core_expand_chado_vars().
  1440. *
  1441. * Criteria are php strings that evaluate to either TRUE or FALSE. These strings are evaluated using
  1442. * drupal_eval() which suppresses syntax errors and throws watchdog entries of type php. There are
  1443. * also watchdog entries of type tripal_core stating the exact criteria evaluated. Criteria can
  1444. * contain the following tokens:
  1445. * - &gt;field_name&lt;
  1446. * Replaced by the name of the field to be excluded
  1447. * - &gt;field_value&lt;
  1448. * Replaced by the value of the field in the current record
  1449. * Also keep in mind that if your criteria doesn't contain the &gt;field_value&lt; token then it will be
  1450. * evaluated before the query is executed and if the field is excluded it won't be included in the
  1451. * query.
  1452. *
  1453. * @return
  1454. * An array of type => criteria where the type is excluded if the criteria evaluates to TRUE
  1455. *
  1456. * @ingroup tripal_chado_api
  1457. */
  1458. function tripal_core_exclude_field_from_feature_by_default() {
  1459. return array();
  1460. }
  1461. /**
  1462. * Use this function instead of db_query() to avoid switching databases
  1463. * when making query to the chado database
  1464. *
  1465. * Will use a chado persistent connection if it already exists
  1466. *
  1467. * @param $sql
  1468. * The sql statement to execute
  1469. */
  1470. function chado_query($sql) {
  1471. global $active_db;
  1472. $args = func_get_args();
  1473. array_shift($args);
  1474. $sql = db_prefix_tables($sql);
  1475. if (isset($args[0]) and is_array($args[0])) { // 'All arguments in one array' syntax
  1476. $args = $args[0];
  1477. }
  1478. _db_query_callback($args, TRUE);
  1479. $sql = preg_replace_callback(DB_QUERY_REGEXP, '_db_query_callback', $sql);
  1480. // Execute the query on the chado database/schema
  1481. // Use the persistent chado connection if it already exists
  1482. $persistent_connection = variable_get('tripal_perisistent_chado', NULL);
  1483. if ($persistent_connection) {
  1484. $previously_active_db = $active_db;
  1485. $active_db = $persistent_connection;
  1486. $results = _db_query($sql);
  1487. $active_db = $previously_active_db;
  1488. }
  1489. else {
  1490. $previous_db = tripal_db_set_active('chado');
  1491. $results = _db_query($sql);
  1492. tripal_db_set_active($previous_db);
  1493. }
  1494. return $results;
  1495. }
  1496. /**
  1497. * Get chado id for a node. E.g, if you want to get 'analysis_id' from the
  1498. * 'analysis' table for a synced 'chado_analysis' node, use:
  1499. * $analysis_id = chado_get_id_for_node ('analysis', $node)
  1500. * Likewise,
  1501. * $organism_id = chado_get_id_for_node ('organism', $node)
  1502. * $feature_id = chado_get_id_for_node ('feature', $node)
  1503. */
  1504. function chado_get_id_for_node($table, $node) {
  1505. return db_result(db_query("SELECT %s_id FROM {chado_%s} WHERE nid = %d", $table, $table, $node->nid));
  1506. }
  1507. /**
  1508. * Get node id for a chado feature/organism/analysis. E.g, if you want to
  1509. * get the node id for an analysis, use:
  1510. * $nid = chado_get_node_id ('analysis', $analysis_id)
  1511. * Likewise,
  1512. * $nid = chado_get_node_id ('organism', $organism_id)
  1513. * $nid = chado_get_node_id ('feature', $feature_id)
  1514. */
  1515. function chado_get_node_id($table, $id) {
  1516. return db_result(db_query("SELECT nid FROM {chado_%s} WHERE %s_id = %d", $table, $table, $id));
  1517. }
  1518. /**
  1519. * Retrieve a property for a given base table record
  1520. *
  1521. * @param $basetable
  1522. * The base table for which the property should be retrieved. Thus to retrieve a property
  1523. * for a feature the basetable=feature and property is retrieved from featureprop
  1524. * @param $record_id
  1525. * The foriegn key field of the base table. This should be in integer.
  1526. * @param $property
  1527. * The cvterm name describing the type of properties to be retrieved
  1528. * @param $cv_name
  1529. * The name of the cv that the above cvterm is part of
  1530. *
  1531. * @return
  1532. * An array in the same format as that generated by the function
  1533. * tripal_core_generate_chado_var(). If only one record is returned it
  1534. * is a single object. If more than one record is returned then it is an array
  1535. * of objects
  1536. *
  1537. * @ingroup tripal_chado_api
  1538. */
  1539. function tripal_core_get_property($basetable, $record_id, $property, $cv_name) {
  1540. // get the foreign key for this property table
  1541. $table_desc = tripal_core_get_chado_table_schema($basetable . 'prop');
  1542. $fkcol = key($table_desc['foreign keys'][$basetable]['columns']);
  1543. // construct the array of values to be selected
  1544. $values = array(
  1545. $fkcol => $record_id,
  1546. 'type_id' => array(
  1547. 'cv_id' => array(
  1548. 'name' => $cv_name,
  1549. ),
  1550. 'name' => $property,
  1551. 'is_obsolete' => 0
  1552. ),
  1553. );
  1554. $results = tripal_core_generate_chado_var($basetable . 'prop', $values);
  1555. $results = tripal_core_expand_chado_vars($results, 'field', $basetable . 'prop.value');
  1556. return $results;
  1557. }
  1558. /**
  1559. * Insert a property for a given base table. By default if the property already
  1560. * exists a new property is added with the next available rank. If
  1561. * $update_if_present argument is specified then the record will be updated if it
  1562. * exists rather than adding a new property.
  1563. *
  1564. * @param $basetable
  1565. * The base table for which the property should be inserted. Thus to insert a property
  1566. * for a feature the basetable=feature and property is inserted into featureprop
  1567. * @param $record_id
  1568. * The foriegn key field of the base table. This should be in integer.
  1569. * @param $property
  1570. * The cvterm name describing the type of properties to be inserted
  1571. * @param $cv_name
  1572. * The name of the cv that the above cvterm is part of
  1573. * @param $value
  1574. * The value of the property to be inserted (can be empty)
  1575. * @param $update_if_present
  1576. * A boolean indicating whether an existing record should be updated. If the
  1577. * property already exists and this value is not specified or is zero then
  1578. * a new property will be added with the next largest rank.
  1579. *
  1580. * @return
  1581. * Return True on Insert/Update and False otherwise
  1582. *
  1583. * @ingroup tripal_chado_api
  1584. */
  1585. function tripal_core_insert_property($basetable, $record_id, $property,
  1586. $cv_name, $value, $update_if_present = 0) {
  1587. // first see if the property already exists, if the user want's to update
  1588. // then we can do that, but otherwise we want to increment the rank and
  1589. // insert
  1590. $props = tripal_core_get_property($basetable, $record_id, $property, $cv_name);
  1591. if (!is_array($props)) {
  1592. $props = array($props);
  1593. }
  1594. $rank = 0;
  1595. if (count($props)>0) {
  1596. if ($update_if_present) {
  1597. return tripal_core_update_property($basetable, $record_id, $property, $cv_name, $value);
  1598. }
  1599. else {
  1600. // iterate through the properties returned and check to see if the
  1601. // property with this value already exists if not, get the largest rank
  1602. // and insert the same property but with this new value
  1603. foreach ($props as $p) {
  1604. if ($p->rank > $rank) {
  1605. $rank = $p->rank;
  1606. }
  1607. if (strcmp($p->value, $value) == 0) {
  1608. return TRUE;
  1609. }
  1610. }
  1611. // now add 1 to the rank
  1612. $rank++;
  1613. }
  1614. }
  1615. // get the foreign key for this property table
  1616. $table_desc = tripal_core_get_chado_table_schema($basetable . 'prop');
  1617. $fkcol = key($table_desc['foreign keys'][$basetable]['columns']);
  1618. // construct the array of values to be inserted
  1619. $values = array(
  1620. $fkcol => $record_id,
  1621. 'type_id' => array(
  1622. 'cv_id' => array(
  1623. 'name' => $cv_name,
  1624. ),
  1625. 'name' => $property,
  1626. 'is_obsolete' => 0
  1627. ),
  1628. 'value' => $value,
  1629. 'rank' => $rank,
  1630. );
  1631. return tripal_core_chado_insert($basetable . 'prop', $values);
  1632. }
  1633. /**
  1634. * Update a property for a given base table record and property name. This
  1635. * function should be used only if one record of the property will be present.
  1636. * If the property name can have multiple entries (with increasing rank) then
  1637. * use the function named tripal_core_update_property_by_id
  1638. *
  1639. * @param $basetable
  1640. * The base table for which the property should be updated. The property table
  1641. * is constructed using a combination of the base table name and the suffix
  1642. * 'prop' (e.g. basetable = feature then property tabie is featureprop).
  1643. * @param $record_id
  1644. * The foreign key of the basetable to update a property for. This should be in integer.
  1645. * For example, if the basetable is 'feature' then the $record_id should be the feature_id
  1646. * @param $property
  1647. * The cvterm name of property to be updated
  1648. * @param $cv_name
  1649. * The name of the cv that the above cvterm is part of
  1650. * @param $value
  1651. * The value of the property to be inserted (can be empty)
  1652. * @param $insert_if_missing
  1653. * A boolean indicating whether a record should be inserted if one doesn't exist to update
  1654. *
  1655. * Note: The property to be updated is select via the unique combination of $record_id and
  1656. * $property and then it is updated with the supplied value
  1657. *
  1658. * @return
  1659. * Return True on Update/Insert and False otherwise
  1660. *
  1661. * @ingroup tripal_chado_api
  1662. */
  1663. function tripal_core_update_property($basetable, $record_id, $property,
  1664. $cv_name, $value, $insert_if_missing = 0) {
  1665. // first see if the property is missing (we can't update a missing property
  1666. $prop = tripal_core_get_property($basetable, $record_id, $property, $cv_name);
  1667. if (count($prop)==0) {
  1668. if ($insert_if_missing) {
  1669. return tripal_core_insert_property($basetable, $record_id, $property, $cv_name, $value);
  1670. }
  1671. else {
  1672. return FALSE;
  1673. }
  1674. }
  1675. // get the foreign key for this property table
  1676. $table_desc = tripal_core_get_chado_table_schema($basetable . 'prop');
  1677. $fkcol = key($table_desc['foreign keys'][$basetable]['columns']);
  1678. // construct the array that will match the exact record to update
  1679. $match = array(
  1680. $fkcol => $record_id,
  1681. 'type_id' => array(
  1682. 'cv_id' => array(
  1683. 'name' => $cv_name,
  1684. ),
  1685. 'name' => $property,
  1686. ),
  1687. );
  1688. // construct the array of values to be updated
  1689. $values = array(
  1690. 'value' => $value,
  1691. );
  1692. return tripal_core_chado_update($basetable . 'prop', $match, $values);
  1693. }
  1694. /**
  1695. * Update a property for a given base table record. This function should be
  1696. * used if multiple records of the same property will be present. Also, use this
  1697. * function to change the property name of an existing property.
  1698. *
  1699. * @param $basetable
  1700. * The base table for which the property should be updated. The property table
  1701. * is constructed using a combination of the base table name and the suffix
  1702. * 'prop' (e.g. basetable = feature then property tabie is featureprop).
  1703. * @param $record_id
  1704. * The primary key of the base table. This should be in integer.
  1705. * For example, if the basetable is 'feature' then the $record_id should be the featureprop_id
  1706. * @param $property
  1707. * The cvterm name of property to be updated
  1708. * @param $cv_name
  1709. * The name of the cv that the above cvterm is part of
  1710. * @param $value
  1711. * The value of the property to be inserted (can be empty)
  1712. *
  1713. * @return
  1714. * Return True on Update/Insert and False otherwise
  1715. *
  1716. * @ingroup tripal_chado_api
  1717. */
  1718. function tripal_core_update_property_by_id($basetable, $record_id, $property,
  1719. $cv_name, $value) {
  1720. // get the primary key for this property table
  1721. $table_desc = tripal_core_get_chado_table_schema($basetable . 'prop');
  1722. $pkcol = $table_desc['primary key'][0];
  1723. // construct the array that will match the exact record to update
  1724. $match = array(
  1725. $pkcol => $record_id,
  1726. );
  1727. // construct the array of values to be updated
  1728. $values = array(
  1729. 'type_id' => array(
  1730. 'cv_id' => array(
  1731. 'name' => $cv_name,
  1732. ),
  1733. 'name' => $property,
  1734. ),
  1735. 'value' => $value,
  1736. );
  1737. return tripal_core_chado_update($basetable . 'prop', $match, $values);
  1738. }
  1739. /**
  1740. * Deletes a property for a given base table record using the property name
  1741. *
  1742. * @param $basetable
  1743. * The base table for which the property should be deleted. Thus to deleted a property
  1744. * for a feature the basetable=feature and property is deleted from featureprop
  1745. * @param $record_id
  1746. * The primary key of the basetable to delete a property for. This should be in integer.
  1747. * @param $property
  1748. * The cvterm name describing the type of property to be deleted
  1749. * @param $cv_name
  1750. * The name of the cv that the above cvterm is part of
  1751. *
  1752. * Note: The property to be deleted is select via the unique combination of $record_id and $property
  1753. *
  1754. * @return
  1755. * Return True on Delete and False otherwise
  1756. *
  1757. * @ingroup tripal_chado_api
  1758. */
  1759. function tripal_core_delete_property($basetable, $record_id, $property, $cv_name) {
  1760. // get the foreign key for this property table
  1761. $table_desc = tripal_core_get_chado_table_schema($basetable . 'prop');
  1762. $fkcol = key($table_desc['foreign keys'][$basetable]['columns']);
  1763. // construct the array that will match the exact record to update
  1764. $match = array(
  1765. $fkcol => $record_id,
  1766. 'type_id' => array(
  1767. 'cv_id' => array(
  1768. 'name' => $cv_name,
  1769. ),
  1770. 'name' => $property,
  1771. ),
  1772. );
  1773. return tripal_core_chado_delete($basetable . 'prop', $match);
  1774. }
  1775. /**
  1776. * Deletes a property using the property ID
  1777. *
  1778. * @param $basetable
  1779. * The base table for which the property should be deleted. Thus to deleted a property
  1780. * for a feature the basetable=feature and property is deleted from featureprop
  1781. * @param $record_id
  1782. * The primary key of the basetable to delete a property for. This should be in integer.
  1783. *
  1784. * @return
  1785. * Return True on Delete and False otherwise
  1786. *
  1787. * @ingroup tripal_chado_api
  1788. */
  1789. function tripal_core_delete_property_by_id($basetable, $record_id) {
  1790. // get the foreign key for this property table
  1791. $table_desc = tripal_core_get_chado_table_schema($basetable . 'prop');
  1792. $pkcol = $table_desc['primary key'][0];
  1793. // construct the array that will match the exact record to update
  1794. $match = array(
  1795. $pkcol => $record_id,
  1796. );
  1797. return tripal_core_chado_delete($basetable . 'prop', $match);
  1798. }
  1799. /**
  1800. * This function is typically used in the '.install' file for a Tripal module
  1801. * Each module should call this function during installation to create
  1802. * the module data directory which is sites/default/files/tripal/[module_name]
  1803. * for default Drupal settings. This directory can then be used by the module
  1804. * for storing files.
  1805. *
  1806. * @param $module_name
  1807. * the name of the module being installed.
  1808. *
  1809. * @returns
  1810. * nothing
  1811. *
  1812. * @ingroup tripal_files_api
  1813. */
  1814. function tripal_create_moddir($module_name) {
  1815. // make the data directory for this module
  1816. $data_dir = file_directory_path() . "/tripal/$module_name";
  1817. if (!file_check_directory($data_dir, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS)) {
  1818. $message = "Cannot create directory $data_dir. This module may not ".
  1819. "behave correctly without this directory. Please create ".
  1820. "the directory manually or fix the problem and reinstall.";
  1821. drupal_set_message(check_plain(t($message)), 'error');
  1822. watchdog('tripal_core', $message, array(), WATCHDOG_ERROR);
  1823. }
  1824. }
  1825. /**
  1826. * Each Tripal module has a unique data directory which was creatd using the
  1827. * tripal_create_moddir function during installation. This function
  1828. * retrieves the directory path.
  1829. *
  1830. * @param $module_name
  1831. * The name of the module
  1832. *
  1833. * @returns
  1834. * The path within the Drupal installation where the data directory resides
  1835. * @ingroup tripal_files_api
  1836. */
  1837. function tripal_get_moddir($module_name) {
  1838. $data_dir = file_directory_path() . "/tripal/$module_name";
  1839. return $data_dir;
  1840. }
  1841. /**
  1842. * Set the Tripal Database
  1843. *
  1844. * The tripal_db_set_active function is used to prevent namespace collisions
  1845. * when chado and drupal are installed in the same database but in different
  1846. * schemas. It is also used for backwards compatibility with older versions
  1847. * of tripal or in cases where chado is located outside of the Drupal database.
  1848. *
  1849. * @ingroup tripal_chado_api
  1850. */
  1851. function tripal_db_set_active($dbname) {
  1852. global $db_url, $db_type;
  1853. $chado_exists = 0;
  1854. // only postgres can support search paths. So if this is MysQL then
  1855. // just run the normal tripal_db_set_active function.
  1856. if (strcmp($db_type, 'pgsql')==0) {
  1857. // if the 'chado' database is in the $db_url variable then chado is
  1858. // not in the same Drupal database
  1859. if (is_array($db_url)) {
  1860. if (isset($db_url[$dbname])) {
  1861. return db_set_active($dbname);
  1862. }
  1863. }
  1864. // check to make sure the chado schema exists
  1865. $chado_exists = tripal_core_chado_schema_exists();
  1866. // here we make the assumption that the default database schema is
  1867. // 'public'. This will most likely always be the case but if not,
  1868. // then this code will break
  1869. if ($chado_exists && strcmp($dbname, 'chado')==0) {
  1870. db_query("set search_path to %s", 'chado,public');
  1871. return 'public,chado';
  1872. }
  1873. elseif ($chado_exists) {
  1874. db_query("set search_path to %s", 'public,chado');
  1875. return 'chado,public';
  1876. }
  1877. else {
  1878. return db_set_active($dbname);
  1879. }
  1880. }
  1881. else {
  1882. return db_set_active($dbname);
  1883. }
  1884. }
  1885. /**
  1886. * Indicates if the SQL statement is prepapred
  1887. *
  1888. * @param $statement_name
  1889. * The name of the statement to check if it is prepared.
  1890. *
  1891. * @return
  1892. * TRUE if the statement is preapred, FALSE otherwise
  1893. */
  1894. function tripal_core_is_sql_prepared($statement_name) {
  1895. // @coder-ignore: acting on postgres tables rather then drupal schema therefore, table prefixing does not apply
  1896. $sql = "SELECT name FROM pg_prepared_statements WHERE name = '%s'";
  1897. $result = db_fetch_object(chado_query($sql, $statement_name));
  1898. if ($result) {
  1899. return TRUE;
  1900. }
  1901. return FALSE;
  1902. }
  1903. /**
  1904. * Instantiate or Return a persistent chado connection
  1905. *
  1906. * NOTE: cannot use $active_db since a new connection is created each time
  1907. * db_set_active() is called
  1908. *
  1909. * @return
  1910. * A postgresql connection object which can be used by pg_prepare, pg_execute, etc.
  1911. */
  1912. function tripal_db_persistent_chado() {
  1913. global $db_url;
  1914. // get connection if it already exists
  1915. $connection = variable_get('tripal_perisistent_chado', NULL);
  1916. if ($connection) {
  1917. return $connection;
  1918. // Otherwise we need to set it
  1919. }
  1920. else {
  1921. if (is_array($db_url) && isset($db_url['chado'])) {
  1922. $connection = db_connect($db_url['chado']);
  1923. variable_set('tripal_perisistent_chado', $connection);
  1924. }
  1925. else {
  1926. $connection = db_connect($db_url);
  1927. variable_set('tripal_perisistent_chado', $connection);
  1928. }
  1929. return $connection;
  1930. }
  1931. return FALSE;
  1932. }
  1933. /**
  1934. * Release a persistent chado connection
  1935. */
  1936. function tripal_db_release_persistent_chado() {
  1937. variable_del('tripal_perisistent_chado');
  1938. }
  1939. /**
  1940. * Start a transaction block. Ensures the use of a persistent chado connection
  1941. */
  1942. function tripal_db_start_transaction() {
  1943. $connection = tripal_db_persistent_chado();
  1944. chado_query("BEGIN");
  1945. }
  1946. /**
  1947. * Set a savepoint to roll the current transaction back to if an error is encountered
  1948. */
  1949. function tripal_db_set_savepoint_transaction($savepoint, $release = FALSE) {
  1950. // Postgresql requires a savepoint of the same name to be unset before re-use
  1951. if ($release) {
  1952. chado_query("RELEASE SAVEPOINT %s", $savepoint);
  1953. }
  1954. chado_query("SAVEPOINT %s", $savepoint);
  1955. }
  1956. /**
  1957. * Commit changes made during the current transaction
  1958. */
  1959. function tripal_db_commit_transaction() {
  1960. chado_query("COMMIT");
  1961. }
  1962. /**
  1963. * Rollback changes.
  1964. *
  1965. * If $savepoint is NULL then rollback to the beginning of the transaction,
  1966. * Otherwise, rollback to the point at which the named $savepoint was created
  1967. *
  1968. * @param $savepoint
  1969. * The name of the saved point in the transaction to rollback to
  1970. */
  1971. function tripal_db_rollback_transaction($savepoint = NULL, $commit = TRUE) {
  1972. if ($savepoint) {
  1973. chado_query("ROLLBACK TO SAVEPOINT %s", $savepoint);
  1974. }
  1975. else {
  1976. chado_query("ROLLBACK");
  1977. }
  1978. if ($commit) {
  1979. tripal_db_commit_transaction();
  1980. }
  1981. }
  1982. /**
  1983. * Purpose: Get max rank for a given set of criteria
  1984. * This function was developed with the many property tables in chado in mind
  1985. *
  1986. * @param $tablename
  1987. * The name of the chado table you want to select the max rank from this table must contain a
  1988. * rank column of type integer
  1989. * @param $where_options
  1990. * where options should include the id and type for that table to correctly
  1991. * group a set of records together where the only difference are the value and rank
  1992. * @code
  1993. * array(
  1994. * <column_name> => array(
  1995. * 'type' => <type of column: INT/STRING>,
  1996. * 'value' => <the value you want to filter on>,
  1997. * 'exact' => <if TRUE use =; if FALSE use ~>,
  1998. * )
  1999. * )
  2000. * @endcode
  2001. * @return the maximum rank
  2002. *
  2003. * @ingroup tripal_chado_api
  2004. */
  2005. function tripal_get_max_chado_rank($tablename, $where_options) {
  2006. $where= array();
  2007. //generate the where clause from supplied options
  2008. // the key is the column name
  2009. foreach ($where_options as $key => $val_array) {
  2010. if (preg_match('/INT/', $val_array['type'])) {
  2011. $where[] = $key . "=" . $val_array['value'];
  2012. }
  2013. else {
  2014. if ($val_array['exact']) {
  2015. $operator='=';
  2016. }
  2017. else {
  2018. $operator='~';
  2019. }
  2020. $where[] = $key . $operator . "'" . $val_array['value'] . "'";
  2021. }
  2022. }
  2023. $previous_db = tripal_db_set_active('chado');
  2024. $result = db_fetch_object(db_query(
  2025. "SELECT max(rank) as max_rank, count(rank) as count FROM %s WHERE %s",
  2026. $tablename,
  2027. implode(' AND ', $where)
  2028. ));
  2029. tripal_db_set_active($previous_db);
  2030. //drupal_set_message("Max Rank Query=SELECT max(rank) as max_rank, count(rank) as count FROM ".$tablename." WHERE ".implode(' AND ',$where));
  2031. if ($result->count > 0) {
  2032. return $result->max_rank;
  2033. }
  2034. else {
  2035. return -1;
  2036. }
  2037. }
  2038. /**
  2039. * Add a new table to the Chado schema. This function is simply a wrapper for
  2040. * the db_create_table() function of Drupal, but ensures the table is created
  2041. * inside the Chado schema rather than the Drupal schema. If the table already
  2042. * exists then it will be dropped and recreated using the schema provided.
  2043. * Howver, it will only drop a table if it exsits in the tripal_custom_tables
  2044. * table. This way the function cannot be used to accidentally alter existing
  2045. * non custom tables.
  2046. *
  2047. * @param $ret
  2048. * Array to which query results will be added.
  2049. * @param $table
  2050. * The name of the table to create.
  2051. * @param $schema
  2052. * A Drupal-style Schema API definition of the table
  2053. *
  2054. * @return
  2055. * A database query result resource for the new table, or FALSE if table was not constructed.
  2056. *
  2057. * @ingroup tripal_core_api
  2058. */
  2059. function tripal_create_chado_table(&$ret, $table, $schema) {
  2060. $ret = array();
  2061. // If the table exits in Chado but not in the tripal_custom_tables field
  2062. // then call an error. if the table exits in the tripal_custom_tables but
  2063. // not in Chado then create the table and replace the entry.
  2064. $sql = "SELECT * FROM {tripal_custom_tables} WHERE table_name = '%s'";
  2065. $centry = db_fetch_object(db_query($sql, $table));
  2066. $previous_db = tripal_db_set_active('chado'); // use chado database
  2067. $exists = db_table_exists($table);
  2068. tripal_db_set_active($previous_db); // now use drupal database
  2069. if (!$exists) {
  2070. $previous_db = tripal_db_set_active('chado'); // use chado database
  2071. db_create_table($ret, $table, $schema);
  2072. tripal_db_set_active($previous_db); // now use drupal database
  2073. if (count($ret)==0) {
  2074. watchdog('tripal_core', "Error adding custom table '!table_name'.",
  2075. array('!table_name' => $table), WATCHDOG_ERROR);
  2076. return FALSE;
  2077. }
  2078. }
  2079. if ($exists and !$centry) {
  2080. watchdog('tripal_core', "Could not add custom table '!table_name'. It ".
  2081. "already exists but is not known to Tripal as being a custom table.",
  2082. array('!table_name' => $table), WATCHDOG_WARNING);
  2083. return FALSE;
  2084. }
  2085. if ($exists and $centry) {
  2086. // drop the table we'll recreate it with the new schema
  2087. $previous_db = tripal_db_set_active('chado'); // use chado database
  2088. db_drop_table($ret, $table);
  2089. db_create_table($ret, $table, $schema);
  2090. tripal_db_set_active($previous_db); // now use drupal database
  2091. }
  2092. // if the table creation was succesful then add an entry
  2093. // in the tripal_custom_table
  2094. $record = new stdClass();
  2095. $record->table_name = $table;
  2096. $record->schema = serialize($schema);
  2097. // if an entry already exists then remove it
  2098. if ($centry) {
  2099. $sql = "DELETE FROM {tripal_custom_tables} WHERE table_name = '%s'";
  2100. db_query($sql, $table);
  2101. }
  2102. $success = drupal_write_record('tripal_custom_tables', $record);
  2103. if (!$success) {
  2104. watchdog('tripal_core', "Error adding custom table.",
  2105. array('!table_name' => $table), WATCHDOG_ERROR);
  2106. return FALSE;
  2107. }
  2108. return $ret;
  2109. }
  2110. /**
  2111. * Retrieves the schema in an array for the specified custom table.
  2112. *
  2113. * @param $table
  2114. * The name of the table to create.
  2115. *
  2116. * @return
  2117. * A Drupal-style Schema API array definition of the table. Returns
  2118. * FALSE on failure.
  2119. *
  2120. * @ingroup tripal_core_api
  2121. */
  2122. function tripal_get_chado_custom_schema($table) {
  2123. $sql = "SELECT schema FROM {tripal_custom_tables} WHERE table_name = '%s'";
  2124. $custom = db_fetch_object(db_query($sql, $table));
  2125. if (!$custom) {
  2126. return FALSE;
  2127. }
  2128. else {
  2129. return unserialize($custom->schema);
  2130. }
  2131. }
  2132. /**
  2133. * Check that the Chado schema exists
  2134. *
  2135. * @return
  2136. * TRUE/FALSE depending upon whether it exists
  2137. */
  2138. function tripal_core_chado_schema_exists() {
  2139. // This is postgresql-specific code to check the existence of the chado schema
  2140. // @coder-ignore: acting on pg_catalog schema rather then drupal schema therefore, table prefixing does not apply
  2141. $sql = "SELECT nspname FROM pg_catalog.pg_namespace WHERE nspname = 'chado'";
  2142. if (db_fetch_object(db_query($sql))) {
  2143. return TRUE;
  2144. }
  2145. else {
  2146. return FALSE;
  2147. }
  2148. }
  2149. /**
  2150. * Retrieves the list tables in the Chado schema. By default it only retursn
  2151. * the default Chado tables, but may also return custom tables added to the
  2152. * Chado schema as well.
  2153. *
  2154. * @param $include_custom
  2155. * Optional. Set as TRUE to include any custom tables created in the
  2156. * Chado schema. Custom tables are added to Chado using the
  2157. * tripal_core_chado_create_table() function.
  2158. *
  2159. * @returns
  2160. * An associative array where the key and value pairs are the Chado table names.
  2161. *
  2162. * @ingroup tripal_core_api
  2163. */
  2164. function tripal_core_get_chado_tables($include_custom = NULL) {
  2165. if (is_array($db_url) AND array_key_exists('chado', $db_url)) {
  2166. $previous_db = tripal_db_set_active('chado');
  2167. // @coder-ignore: acting on pg_catalog schema rather then drupal schema therefore, table prefixing does not apply
  2168. $sql = 'SELECT tablename FROM pg_tables';
  2169. $resource = db_query($sql);
  2170. tripal_db_set_active($previous_db);
  2171. }
  2172. else {
  2173. // @coder-ignore: acting on pg_catalog schema rather then drupal schema therefore, table prefixing does not apply
  2174. $sql = "SELECT tablename FROM pg_tables WHERE schemaname='chado'";
  2175. $resource = db_query($sql);
  2176. }
  2177. $tables = array();
  2178. while ($r = db_fetch_object($resource)) {
  2179. $tables[$r->tablename] = $r->tablename;
  2180. }
  2181. // now add in the custom tables too
  2182. if ($include_custom) {
  2183. $sql = "SELECT table_name FROM {tripal_custom_tables}";
  2184. $resource = db_query($sql);
  2185. }
  2186. while ($r = db_fetch_object($resource)) {
  2187. $tables[$r->table_name] = $r->table_name;
  2188. }
  2189. asort($tables);
  2190. return $tables;
  2191. }
  2192. /**
  2193. * Queries the database to detrmine the Chado version and sets
  2194. * a Drupal variable named 'chado_version'.
  2195. *
  2196. * @returns
  2197. * The version of Chado
  2198. *
  2199. * @ingroup tripal_core_api
  2200. */
  2201. function tripal_core_set_chado_version() {
  2202. // check that Chado is installed if not return 'uninstalled as the version'
  2203. $chado_exists = tripal_core_chado_schema_exists();
  2204. if (!$chado_exists) {
  2205. return 'not installed';
  2206. }
  2207. // if the table doesn't exist then we don't know what version but we know
  2208. // it must be 1.11 or older.
  2209. $previous_db = tripal_db_set_active('chado');
  2210. $prop_exists = db_table_exists('chadoprop');
  2211. tripal_db_set_active($previous_db);
  2212. if (!$prop_exists) {
  2213. drupal_set_message(t("WARNING: Tripal does not fully support Chado version less than v1.11. If you are certain this is v1.11
  2214. of if Chado was installed using Tripal v0.3.1b then all is well. If not please check the version and either upgrade to
  2215. v1.11 or a later version"),'warning');
  2216. variable_set('chado_version', "1.11 or older");
  2217. return "1.11 or older";
  2218. }
  2219. // we can't use the Tripal API to query this table
  2220. // because the Tripal API depends on this fucntion to
  2221. // tell it the version. So, we need a typical SQL statement
  2222. $sql = "SELECT value "
  2223. ."FROM chadoprop CP "
  2224. ." INNER JOIN cvterm CVT on CVT.cvterm_id = CP.type_id "
  2225. ." INNER JOIN cv CV on CVT.cv_id = CV.cv_id "
  2226. ."WHERE CV.name = 'chado_properties' and CVT.name = 'version'";
  2227. $previous_db = tripal_db_set_active('chado');
  2228. $v = db_fetch_object(db_query($sql));
  2229. $previous_db = tripal_db_set_active('chado');
  2230. // if we don't have a version in the chadoprop table then it must be
  2231. // v1.11 or older
  2232. if (!$v->value) {
  2233. drupal_set_message(t("WARNING: Tripal does not fully support Chado version less than v1.11. If you are certain this is v1.11
  2234. of if Chado was installed using Tripal v0.3.1b then all is well. If not please check the version and either upgrade to
  2235. v1.11 or a later version"),'warning');
  2236. variable_set('chado_version', "1.11 or older");
  2237. return "1.11 or older";
  2238. }
  2239. if($v->value != '1.11' and $v->value != '1.2'){
  2240. drupal_set_message(t("WARNING: This currently installed version of Chado is not fully supported."),'warning');
  2241. }
  2242. variable_set('chado_version', "1.11 or older");
  2243. return $v->value;
  2244. }
  2245. /**
  2246. * Retrieves the chado tables Schema API array.
  2247. *
  2248. * @param $table
  2249. * The name of the table to retrieve. The function will use the appopriate
  2250. * Tripal chado schema API hooks (e.g. v1.11 or v1.2).
  2251. *
  2252. * @returns
  2253. * A Drupal Schema API array defining the table.
  2254. *
  2255. * @ingroup tripal_core_api
  2256. */
  2257. function tripal_core_get_chado_table_schema($table) {
  2258. // first get the chado version that is installed
  2259. $v = variable_get('chado_version', '');
  2260. if (!$v) {
  2261. $v = tripal_core_set_chado_version();
  2262. }
  2263. // Tripal only supports v1.11 or newer
  2264. if (strcmp($v, '1.11 or older') == 0) {
  2265. $v = "1.11";
  2266. }
  2267. // get the table array from the proper chado schema
  2268. $v = preg_replace("/\./", "_", $v); // reformat version for hook name
  2269. $table_arr = module_invoke_all("chado_schema_v" . $v . "_" . $table);
  2270. return $table_arr;
  2271. }