tripal_core_chado.api.inc 126 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502
  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. *
  12. * @defgroup tripal_chado_api Chado API
  13. * @ingroup tripal_core_api
  14. * @{
  15. * Provides an application programming interface (API) to manage data withing the Chado database.
  16. * This includes functions for selecting, inserting, updating and deleting records
  17. * in Chado tables. The functions will ensure proper integrity contraints are met
  18. * for inserts and updates.
  19. *
  20. * Also, a set of functions is provided for creating template variables. First,
  21. * is the tripal_core_generate_chado_vars which is used to select one ore more
  22. * records from a table and return an array with foreign key relationships fully
  23. * populated. For example, if selecting a feature, the organism_id and type_id
  24. * would be present in the returned array as a nested array with their respective
  25. * foreign keys also nested. The only fields that are not included are text
  26. * fields (which may be very large) or many-to-many foreign key relationships.
  27. * However, these fields and relationships can be expanded using the
  28. * tripal_core_expand_chado_vars.
  29. *
  30. * When a row from a chado table is selected using these two functions, it provides
  31. * a way for users who want to cutomize Drupal template files to access all data
  32. * associate with a specific record.
  33. *
  34. * Finally, the property tables in Chado generally follow the same format. Therefore
  35. * there is a set of functions for inserting, updating and deleting properties for
  36. * any table. This provides quick lookup of properties (provided the CV term is
  37. * known).
  38. *
  39. * @}
  40. *
  41. */
  42. /**
  43. * Provides a generic routine for inserting into any Chado table
  44. *
  45. * Use this function to insert a record into any Chado table. The first
  46. * argument specifies the table for inserting and the second is an array
  47. * of values to be inserted. The array is mutli-dimensional such that
  48. * foreign key lookup values can be specified.
  49. *
  50. * @param $table
  51. * The name of the chado table for inserting
  52. * @param $values
  53. * An associative array containing the values for inserting.
  54. * @param $options
  55. * An array of options such as:
  56. * - skip_validation: TRUE or FALSE. If TRUE will skip all the validation steps and
  57. * just try to insert as is. This is much faster but results in unhandled
  58. * non user-friendly errors if the insert fails.
  59. * - return_record: by default, the function will return the record but with
  60. * the primary keys added after insertion. To simply return TRUE on success
  61. * set this option to FALSE
  62. *
  63. * @return
  64. * On success this function returns the inserted record with the new primary keys
  65. * added to the returned array. On failure, it returns FALSE.
  66. *
  67. * Example usage:
  68. * @code
  69. * $values = array(
  70. * 'organism_id' => array(
  71. * 'genus' => 'Citrus',
  72. * 'species' => 'sinensis',
  73. * ),
  74. * 'name' => 'orange1.1g000034m.g',
  75. * 'uniquename' => 'orange1.1g000034m.g',
  76. * 'type_id' => array (
  77. * 'cv_id' => array (
  78. * 'name' => 'sequence',
  79. * ),
  80. * 'name' => 'gene',
  81. * 'is_obsolete' => 0
  82. * ),
  83. * );
  84. * $result = tripal_core_chado_insert('feature',$values);
  85. * @endcode
  86. * The above code inserts a record into the feature table. The $values array is
  87. * nested such that the organism is selected by way of the organism_id foreign
  88. * key constraint by specifying the genus and species. The cvterm is also
  89. * specified using its foreign key and the cv_id for the cvterm is nested as
  90. * well.
  91. *
  92. * @ingroup tripal_chado_api
  93. */
  94. function tripal_core_chado_insert($table, $values, $options = array()) {
  95. if (!is_array($values)) {
  96. watchdog('tripal_core', 'Cannot pass non array as values for inserting.', array(),
  97. WATCHDOG_ERROR);
  98. return FALSE;
  99. }
  100. if (count($values)==0) {
  101. watchdog('tripal_core', 'Cannot pass an empty array as values for inserting.', array(),
  102. WATCHDOG_ERROR);
  103. return FALSE;
  104. }
  105. // set defaults for options. If we don't set defaults then
  106. // we get memory leaks when we try to access the elements
  107. if (!is_array($options)) {
  108. $options = array();
  109. }
  110. if (!array_key_exists('skip_validation', $options)) {
  111. $options['skip_validation'] = FALSE;
  112. }
  113. if (!array_key_exists('return_record', $options)) {
  114. $options['return_record'] = TRUE;
  115. }
  116. $insert_values = array();
  117. if (array_key_exists('skip_validation', $options)) {
  118. $validate = !$options['skip_validation'];
  119. }
  120. else {
  121. $validate = TRUE;
  122. }
  123. // get the table description
  124. $table_desc = tripal_core_get_chado_table_schema($table);
  125. if (empty($table_desc)) {
  126. watchdog('tripal_core', 'tripal_core_chado_insert: There is no table description for !table_name', array('!table_name' => $table), WATCHDOG_WARNING);
  127. }
  128. // iterate through the values array and create a new 'insert_values' array
  129. // that has all the values needed for insert with all foreign relationsihps
  130. // resolved.
  131. foreach ($values as $field => $value) {
  132. // make sure the field is in the table description. If not then return an error
  133. // message
  134. if (!array_key_exists($field, $table_desc['fields'])) {
  135. watchdog('tripal_core', "tripal_core_chado_insert: The field '%field' does not exist " .
  136. "for the table '%table'. Cannot perform insert. Values: %array",
  137. array('%field' => $field, '%table' => $table, '%array' => print_r($values, 1)), WATCHDOG_ERROR);
  138. return FALSE;
  139. }
  140. if (is_array($value)) {
  141. // select the value from the foreign key relationship for this value
  142. $results = tripal_core_chado_get_foreign_key($table_desc, $field, $value, $foreign_options);
  143. if (sizeof($results) > 1) {
  144. 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);
  145. }
  146. elseif (sizeof($results) < 1) {
  147. //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);
  148. }
  149. else {
  150. $insert_values[$field] = $results[0];
  151. }
  152. }
  153. else {
  154. $insert_values[$field] = $value;
  155. }
  156. }
  157. if ($validate) {
  158. // check for violation of any unique constraints
  159. $ukeys = array();
  160. if (array_key_exists('unique keys', $table_desc)) {
  161. $ukeys = $table_desc['unique keys'];
  162. }
  163. $ukselect_cols = array();
  164. $ukselect_vals = array();
  165. if ($ukeys) {
  166. foreach ($ukeys as $name => $fields) {
  167. foreach ($fields as $index => $field) {
  168. // build the arrays for performing a select that will check the contraint
  169. $ukselect_cols[] = $field;
  170. if (!array_key_exists($field, $insert_values)) {
  171. if (array_key_exists('default', $table_desc['fields'][$field])) {
  172. $ukselect_vals[$field] = $table_desc['fields'][$field]['default'];
  173. }
  174. }
  175. else {
  176. $ukselect_vals[$field] = $insert_values[$field];
  177. }
  178. }
  179. // now check the constraint
  180. if (tripal_core_chado_select($table, $ukselect_cols, $ukselect_vals)) {
  181. watchdog('tripal_core', "tripal_core_chado_insert: Cannot insert duplicate record into $table table: " .
  182. print_r($values, 1), array(), 'WATCHDOG_ERROR');
  183. return FALSE;
  184. }
  185. }
  186. }
  187. // if trying to insert a field that is the primary key, make sure it also is unique
  188. if (array_key_exists('primary key', $table_desc)) {
  189. $pkey = $table_desc['primary key'][0];
  190. if (array_key_exists($pkey, $insert_values)) {
  191. $coptions = array('statement_name' => 'pqsel_' . $table . '_' . $pkey);
  192. if (tripal_core_chado_select($table, array($pkey), array($pkey => $insert_values[$pkey]), $coptions)) {
  193. watchdog('tripal_core', "tripal_core_chado_insert: Cannot insert duplicate primary key into $table table: " . print_r($values, 1), array(), 'WATCHDOG_ERROR');
  194. return FALSE;
  195. }
  196. }
  197. }
  198. // make sure required fields have a value
  199. if (!is_array($table_desc['fields'])) {
  200. $table_desc['fields'] = array();
  201. watchdog('tripal_core', "tripal_core_chado_insert: %table missing fields: \n %schema",
  202. array('%table' => $table, '%schema' => print_r($table_desc, 1)), WATCHDOG_WARNING);
  203. }
  204. foreach ($table_desc['fields'] as $field => $def) {
  205. // a field is considered missing if it cannot be NULL and there is no default
  206. // value for it or it is of type 'serial'
  207. if (array_key_exists('NOT NULL', $def) and
  208. !array_key_exists($field, $insert_values) and
  209. !array_key_exists('default', $def) and
  210. strcmp($def['type'], serial) != 0) {
  211. watchdog('tripal_core', "tripal_core_chado_insert: Field $table.$field cannot be NULL: " .
  212. print_r($values, 1), array(), 'WATCHDOG_ERROR');
  213. return FALSE;
  214. }
  215. }
  216. } //end of validation
  217. // Now build the insert SQL statement
  218. $ifields = array(); // contains the names of the fields
  219. $itypes = array(); // contains placeholders for the sql query
  220. $ivalues = array(); // contains the values of the fields
  221. $i = 1;
  222. foreach ($insert_values as $field => $value) {
  223. $ifields[] = $field;
  224. $ivalues[":$field"] = $value;
  225. $i++;
  226. if (strcmp($value, '__NULL__')==0) {
  227. $itypes[] = "NULL";
  228. }
  229. else {
  230. $itypes[] = ":$field";
  231. }
  232. }
  233. // create the SQL
  234. $sql = 'INSERT INTO {' . $table . '} (' . implode(", ", $ifields) . ") VALUES (" . implode(", ", $itypes) . ")";
  235. $result = chado_query($sql, $ivalues);
  236. // if we have a result then add primary keys to return array
  237. if ($options['return_record'] == TRUE and $result) {
  238. if (array_key_exists('primary key', $table_desc) and is_array($table_desc['primary key'])) {
  239. foreach ($table_desc['primary key'] as $field) {
  240. $sql = "SELECT CURRVAL('{" . $table . "_" . $field . "_seq}')";
  241. $results = chado_query($sql);
  242. $value = $results->fetchObject();
  243. if (!$value) {
  244. watchdog('tripal_core', "tripal_core_chado_insert: not able to retrieve primary key after insert: %sql",
  245. array('%sql' => $sql), WATCHDOG_ERROR);
  246. return FALSE;
  247. }
  248. $values[$field] = $value;
  249. }
  250. }
  251. return $values;
  252. }
  253. elseif ($options['return_record'] == FALSE and $result) {
  254. return TRUE;
  255. }
  256. else {
  257. watchdog('tripal_core', "tripal_core_chado_insert: Cannot insert record into '%table': " . print_r($values, 1),
  258. array('%table' => $table), 'WATCHDOG_ERROR');
  259. return FALSE;
  260. }
  261. return FALSE;
  262. }
  263. /**
  264. * Provides a generic routine for updating into any Chado table
  265. *
  266. * Use this function to update a record in any Chado table. The first
  267. * argument specifies the table for inserting, the second is an array
  268. * of values to matched for locating the record for updating, and the third
  269. * argument give the values to update. The arrays are mutli-dimensional such
  270. * that foreign key lookup values can be specified.
  271. *
  272. * @param $table
  273. * The name of the chado table for inserting
  274. * @param $match
  275. * An associative array containing the values for locating a record to update.
  276. * @param $values
  277. * An associative array containing the values for updating.
  278. * @param $options
  279. * An array of options such as:
  280. * - statement_name: the name of the prepared statement to use. If the statement
  281. * has not yet been prepared it will be prepared automatically. On subsequent
  282. * calls with the same statement_name only an execute on the previously
  283. * prepared statement will occur.
  284. * - is_prepared: TRUE or FALSE. Whether or not the statement is prepared. By
  285. * default if the statement is not prepared it will be automatically.
  286. * However to avoid this check, which requires a database query you can
  287. * set this value to true and the check will not be performed.
  288. * - return_record: by default, the function will return the TRUE if the record
  289. * was succesfully updated. However, set this option to TRUE to return the
  290. * record that was updated. The returned record will have the fields provided
  291. * but the primary key (if available for the table) will be added to the record.
  292. * @return
  293. * On success this function returns TRUE. On failure, it returns FALSE.
  294. *
  295. * Example usage:
  296. * @code
  297. $umatch = array(
  298. 'organism_id' => array(
  299. 'genus' => 'Citrus',
  300. 'species' => 'sinensis',
  301. ),
  302. 'uniquename' => 'orange1.1g000034m.g7',
  303. 'type_id' => array (
  304. 'cv_id' => array (
  305. 'name' => 'sequence',
  306. ),
  307. 'name' => 'gene',
  308. 'is_obsolete' => 0
  309. ),
  310. );
  311. $uvalues = array(
  312. 'name' => 'orange1.1g000034m.g',
  313. 'type_id' => array (
  314. 'cv_id' => array (
  315. 'name' => 'sequence',
  316. ),
  317. 'name' => 'mRNA',
  318. 'is_obsolete' => 0
  319. ),
  320. );
  321. * $result = tripal_core_chado_update('feature',$umatch,$uvalues);
  322. * @endcode
  323. * The above code species that a feature with a given uniquename, organism_id,
  324. * and type_id (the unique constraint for the feature table) will be updated.
  325. * The organism_id is specified as a nested array that uses the organism_id
  326. * foreign key constraint to lookup the specified values to find the exact
  327. * organism_id. The same nested struture is also used for specifying the
  328. * values to update. The function will find the record that matches the
  329. * columns specified and update the record with the avlues in the $uvalues array.
  330. *
  331. * @ingroup tripal_chado_api
  332. */
  333. function tripal_core_chado_update($table, $match, $values, $options = NULL) {
  334. if (!is_array($values)) {
  335. watchdog('tripal_core', 'Cannot pass non array as values for updating.', array(),
  336. WATCHDOG_ERROR);
  337. return FALSE;
  338. }
  339. if (count($values)==0) {
  340. watchdog('tripal_core', 'Cannot pass an empty array as values for updating.', array(),
  341. WATCHDOG_ERROR);
  342. return FALSE;
  343. }
  344. if (!is_array($match)) {
  345. watchdog('tripal_core', 'Cannot pass non array as values for matching.', array(),
  346. WATCHDOG_ERROR);
  347. return FALSE;
  348. }
  349. if (count($match)==0) {
  350. watchdog('tripal_core', 'Cannot pass an empty array as values for matching.', array(),
  351. WATCHDOG_ERROR);
  352. return FALSE;
  353. }
  354. // set defaults for options. If we don't set defaults then
  355. // we get memory leaks when we try to access the elements
  356. if (!is_array($options)) {
  357. $options = array();
  358. }
  359. /*
  360. if (!array_key_exists('is_prepared', $options)) {
  361. $options['is_prepared'] = FALSE;
  362. }
  363. if (!array_key_exists('statement_name', $options)) {
  364. $options['statement_name'] = FALSE;
  365. }
  366. */
  367. if (!array_key_exists('return_record', $options)) {
  368. $options['return_record'] = FALSE;
  369. }
  370. $update_values = array(); // contains the values to be updated
  371. $update_matches = array(); // contains the values for the where clause
  372. /*
  373. // Determine plan of action
  374. if ($options['statement_name']) {
  375. // we have a prepared statment (or want to create one) so set $prepared = TRUE
  376. $prepared = TRUE;
  377. // we need to get a persistent connection. If one exists this function
  378. // will not recreate it, but if not it will create one and store it in
  379. // a Drupal variable for reuse later.
  380. $connection = tripal_db_persistent_chado();
  381. // if we cannot get a connection the abandon the prepared statement
  382. if (!$connection ) {
  383. $prepared = FALSE;
  384. unset($options['statement_name']);
  385. }
  386. }
  387. else {
  388. //print "NO STATEMENT (update): $table\n";
  389. //debug_print_backtrace();
  390. }
  391. */
  392. // get the table description
  393. $table_desc = tripal_core_get_chado_table_schema($table);
  394. // if the user wants us to return the record then we need to get the
  395. // unique primary key if one exists. That way we can add it to the
  396. // values that get returned at the end of the function
  397. $pkeys = array();
  398. if ($options['return_record'] == TRUE) {
  399. if (array_key_exists('primary key', $table_desc) and is_array($table_desc['primary key'])) {
  400. $columns = array();
  401. $stmt_suffix = '';
  402. foreach ($table_desc['primary key'] as $field) {
  403. $columns[] = $field;
  404. $stmt_suffix .= substr($field, 0, 2);
  405. }
  406. $options2 = array('statement_name' => 'sel_' . $table . '_' . $stmt_suffix);
  407. $results = tripal_core_chado_select($table, $columns, $match, $options2);
  408. if (count($results) > 0) {
  409. foreach ($results as $index => $pkey) {
  410. $pkeys[] = $pkey;
  411. }
  412. }
  413. }
  414. }
  415. // get the values needed for matching in the SQL statement
  416. foreach ($match as $field => $value) {
  417. if (is_array($value)) {
  418. $foreign_options = array();
  419. /*
  420. if ($options['statement_name']) {
  421. // add the fk relationship info to the prepared statement name so that
  422. // we can prepare the selects run by the recrusive tripal_core_chado_get_foreign_key
  423. // function.
  424. $fk_sname = "fk_" . $table . "_" . $field;
  425. foreach ($value as $k => $v) {
  426. $fk_sname .= substr($k, 0, 2);
  427. }
  428. $foreign_options['statement_name'] = $fk_sname;
  429. }
  430. */
  431. $results = tripal_core_chado_get_foreign_key($table_desc, $field, $value, $foreign_options);
  432. if (sizeof($results) > 1) {
  433. 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);
  434. }
  435. elseif (sizeof($results) < 1) {
  436. //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);
  437. }
  438. else {
  439. $update_matches[$field] = $results[0];
  440. }
  441. }
  442. else {
  443. $update_matches[$field] = $value;
  444. }
  445. }
  446. // get the values used for updating
  447. foreach ($values as $field => $value) {
  448. if (is_array($value)) {
  449. $foreign_options = array();
  450. // select the value from the foreign key relationship for this value
  451. /*
  452. if ($options['statement_name']) {
  453. // add the fk relationship info to the prepared statement name so that
  454. // we can prepare the selects run by the recrusive tripal_core_chado_get_foreign_key
  455. // function.
  456. $fk_sname = "fk_" . $table . "_" . $field;
  457. foreach ($value as $k => $v) {
  458. $fk_sname .= substr($k, 0, 2);
  459. }
  460. $foreign_options['statement_name'] = $fk_sname;
  461. }
  462. */
  463. $results = tripal_core_chado_get_foreign_key($table_desc, $field, $value, $foreign_options);
  464. if (sizeof($results) > 1) {
  465. 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);
  466. }
  467. elseif (sizeof($results) < 1) {
  468. //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);
  469. }
  470. else {
  471. $update_values[$field] = $results[0];
  472. }
  473. }
  474. else {
  475. $update_values[$field] = $value;
  476. }
  477. }
  478. // now build the SQL statement
  479. $sql = 'UPDATE {' . $table . '} SET ';
  480. $psql = 'UPDATE {' . $table . '} SET ';
  481. $uargs = array();
  482. $idatatypes = array();
  483. $pvalues = array();
  484. $ivalues = array();
  485. $i = 1;
  486. foreach ($update_values as $field => $value) {
  487. if (strcasecmp($table_desc['fields'][$field]['type'], 'serial')==0 OR
  488. strcasecmp($table_desc['fields'][$field]['type'], 'int')==0 OR
  489. strcasecmp($table_desc['fields'][$field]['type'], 'integer')==0) {
  490. if (strcmp($value, '__NULL__') == 0) {
  491. $sql .= " $field = %s, ";
  492. $ivalues[] = 'NULL';
  493. $pvalues[] = '%s';
  494. $uargs[] = 'NULL';
  495. }
  496. else {
  497. $sql .= " $field = %d, ";
  498. $ivalues[] = $value;
  499. $pvalues[] = '%d';
  500. $uargs[] = $value;
  501. }
  502. $idatatypes[] = 'int';
  503. }
  504. elseif (strcasecmp($table_desc['fields'][$field]['type'], 'boolean')==0) {
  505. $sql .= " $field = %s, ";
  506. $pvalues[] = '%s';
  507. if (strcmp($value, '__NULL__')==0) {
  508. $ivalues[] = 'NULL';
  509. $uargs[] = 'NULL';
  510. }
  511. else {
  512. $ivalues[] = $value;
  513. $uargs[] = $value;
  514. }
  515. $idatatypes[] = 'bool';
  516. }
  517. elseif (strcasecmp($table_desc['fields'][$field]['type'], 'float')==0) {
  518. $sql .= " $field = %s, ";
  519. $pvalues[] = '%s';
  520. if (strcmp($value, '__NULL__')==0) {
  521. $ivalues[] = 'NULL';
  522. $uargs[] = 'NULL';
  523. }
  524. else {
  525. $ivalues[] = $value;
  526. $uargs[] = $value;
  527. }
  528. $idatatypes[] = 'numeric';
  529. }
  530. else {
  531. if (strcmp($value, '__NULL__') == 0) {
  532. $sql .= " $field = %s, ";
  533. $ivalues[] = 'NULL';
  534. $uargs[] = 'NULL';
  535. $pvalues[] = '%s';
  536. }
  537. else {
  538. $sql .= " $field = '%s', ";
  539. $ivalues[] = $value;
  540. $uargs[] = $value;
  541. $pvalues[] = "'%s'";
  542. }
  543. $idatatypes[] = 'text';
  544. }
  545. $psql .= "$field = \$" . $i . ", ";
  546. $i++;
  547. }
  548. $sql = drupal_substr($sql, 0, -2); // get rid of the trailing comma & space
  549. $psql = drupal_substr($psql, 0, -2); // get rid of the trailing comma & space
  550. $sql .= " WHERE ";
  551. $psql .= " WHERE ";
  552. foreach ($update_matches as $field => $value) {
  553. if (strcasecmp($table_desc['fields'][$field]['type'], 'serial')==0 OR
  554. strcasecmp($table_desc['fields'][$field]['type'], 'int')==0 OR
  555. strcasecmp($table_desc['fields'][$field]['type'], 'integer')==0) {
  556. if (strcmp($value, '__NULL__')==0) {
  557. $sql .= " $field = %s AND ";
  558. $ivalues[] = 'NULL';
  559. $uargs[] = 'NULL';
  560. $pvalues[] = '%s';
  561. }
  562. else {
  563. $sql .= " $field = %d AND ";
  564. $ivalues[] = $value;
  565. $uargs[] = $value;
  566. $pvalues[] = '%s';
  567. }
  568. $idatatypes[] = 'int';
  569. }
  570. elseif (strcasecmp($table_desc['fields'][$field]['type'], 'boolean')==0) {
  571. $sql .= " $field = %s AND ";
  572. $pvalues[] = '%s';
  573. if (strcmp($value, '__NULL__')==0) {
  574. $ivalues[] = 'NULL';
  575. $uargs[] = 'NULL';
  576. }
  577. else {
  578. $ivalues[] = $value;
  579. $uargs[] = $value;
  580. }
  581. $idatatypes[] = 'bool';
  582. }
  583. elseif (strcasecmp($table_desc['fields'][$field]['type'], 'float')==0) {
  584. $sql .= " $field = %s AND ";
  585. $pvalues[] = '%s';
  586. if (strcmp($value, '__NULL__')==0) {
  587. $ivalues[] = 'NULL';
  588. $uargs[] = 'NULL';
  589. }
  590. else {
  591. $ivalues[] = $value;
  592. $uargs[] = $value;
  593. }
  594. $idatatypes[] = 'numeric';
  595. }
  596. else {
  597. if (strcmp($value, '__NULL__')==0) {
  598. $sql .= " $field = %s AND ";
  599. $ivalues[] = 'NULL';
  600. $uargs[] = 'NULL';
  601. $pvalues[] = '%s';
  602. }
  603. else {
  604. $sql .= " $field = '%s' AND ";
  605. $ivalues[] = $value;
  606. $uargs[] = $value;
  607. $pvalues[] = "'%s'";
  608. }
  609. $idatatypes[] = 'text';
  610. }
  611. $psql .= "$field = \$" . $i . " AND ";
  612. $i++;
  613. }
  614. $sql = drupal_substr($sql, 0, -4); // get rid of the trailing 'AND'
  615. $psql = drupal_substr($psql, 0, -4); // get rid of the trailing 'AND'
  616. // finish constructing the prepared SQL statement
  617. // $psql = "PREPARE " . $options['statement_name'] . " (" . implode(', ', $idatatypes) . ") AS " . $psql;
  618. // finally perform the update. If successful, return the updated record
  619. /*
  620. if ($prepared) {
  621. // if this is the first time we've run this query
  622. // then we need to do the prepare, otherwise just execute
  623. if ($options['is_prepared'] != TRUE and !tripal_core_is_sql_prepared($options['statement_name'])) {
  624. $status = chado_query($psql);
  625. if (!$status) {
  626. watchdog('tripal_core', "tripal_core_chado_update: not able to prepare '%name' statement for: %sql",
  627. array('%name' => $options['statement_name'], '%sql' => $sql), WATCHDOG_ERROR);
  628. return FALSE;
  629. }
  630. }
  631. $sql = "EXECUTE " . $options['statement_name'] . "(" . implode(", ", $pvalues) . ")";
  632. $result = chado_query($sql, $ivalues);
  633. }
  634. // if it's not a prepared statement then insert normally
  635. else {
  636. */
  637. $result = chado_query($sql, $uargs);
  638. // }
  639. // if we have a result then add primary keys to return array
  640. if ($options['return_record'] == TRUE and $result) {
  641. // only if we have a single result do we want to add the primary keys to the values
  642. // array. If the update matched many records we can't add the pkeys
  643. if (count($pkeys) == 1) {
  644. foreach ($pkeys as $index => $pkey) {
  645. foreach ($pkey as $field => $fvalue) {
  646. $values[$field] = $fvalue;
  647. }
  648. }
  649. }
  650. return $values;
  651. }
  652. elseif ($options['return_record'] == FALSE and $result) {
  653. return TRUE;
  654. }
  655. else {
  656. watchdog('tripal_core', "Cannot update record in $table table. \nMatch:" . print_r($match, 1) . "\nValues: " . print_r($values, 1), array(), 'WATCHDOG_ERROR');
  657. return FALSE;
  658. }
  659. return FALSE;
  660. }
  661. /**
  662. * Provides a generic function for deleting a record(s) from any chado table
  663. *
  664. * Use this function to delete a record(s) in any Chado table. The first
  665. * argument specifies the table to delete from and the second is an array
  666. * of values to match for locating the record(s) to be deleted. The arrays
  667. * are mutli-dimensional such that foreign key lookup values can be specified.
  668. *
  669. * @param $table
  670. * The name of the chado table for inserting
  671. * @param $match
  672. * An associative array containing the values for locating a record to update.
  673. * @param $options
  674. * An array of options such as:
  675. * - statement_name: the name of the prepared statement to use. If the statement
  676. * has not yet been prepared it will be prepared automatically. On subsequent
  677. * calls with the same statement_name only an execute on the previously
  678. * prepared statement will occur.
  679. * - is_prepared: TRUE or FALSE. Whether or not the statement is prepared. By
  680. * default if the statement is not prepared it will be automatically.
  681. * However to avoid this check, which requires a database query you can
  682. * set this value to true and the check will not be performed.
  683. * @return
  684. * On success this function returns TRUE. On failure, it returns FALSE.
  685. *
  686. * Example usage:
  687. * @code
  688. $umatch = array(
  689. 'organism_id' => array(
  690. 'genus' => 'Citrus',
  691. 'species' => 'sinensis',
  692. ),
  693. 'uniquename' => 'orange1.1g000034m.g7',
  694. 'type_id' => array (
  695. 'cv_id' => array (
  696. 'name' => 'sequence',
  697. ),
  698. 'name' => 'gene',
  699. 'is_obsolete' => 0
  700. ),
  701. );
  702. $uvalues = array(
  703. 'name' => 'orange1.1g000034m.g',
  704. 'type_id' => array (
  705. 'cv_id' => array (
  706. 'name' => 'sequence',
  707. ),
  708. 'name' => 'mRNA',
  709. 'is_obsolete' => 0
  710. ),
  711. );
  712. * $result = tripal_core_chado_update('feature',$umatch,$uvalues);
  713. * @endcode
  714. * The above code species that a feature with a given uniquename, organism_id,
  715. * and type_id (the unique constraint for the feature table) will be deleted.
  716. * The organism_id is specified as a nested array that uses the organism_id
  717. * foreign key constraint to lookup the specified values to find the exact
  718. * organism_id. The same nested struture is also used for specifying the
  719. * values to update. The function will find all records that match the
  720. * columns specified and delete them.
  721. *
  722. * @ingroup tripal_chado_api
  723. */
  724. function tripal_core_chado_delete($table, $match, $options = NULL) {
  725. if (!is_array($match)) {
  726. watchdog('tripal_core', 'Cannot pass non array as values for matching.', array(),
  727. WATCHDOG_ERROR);
  728. return FALSE;
  729. }
  730. if (count($match)==0) {
  731. watchdog('tripal_core', 'Cannot pass an empty array as values for matching.', array(),
  732. WATCHDOG_ERROR);
  733. return FALSE;
  734. }
  735. // set defaults for options. If we don't set defaults then
  736. // we get memory leaks when we try to access the elements
  737. if (!is_array($options)) {
  738. $options = array();
  739. }
  740. /*
  741. if (!array_key_exists('is_prepared', $options)) {
  742. $options['is_prepared'] = FALSE;
  743. }
  744. if (!array_key_exists('statement_name', $options)) {
  745. $options['statement_name'] = FALSE;
  746. }
  747. // Determine plan of action
  748. if ($options['statement_name']) {
  749. // we have a prepared statment (or want to create one) so set $prepared = TRUE
  750. $prepared = TRUE;
  751. // we need to get a persistent connection. If one exists this function
  752. // will not recreate it, but if not it will create one and store it in
  753. // a Drupal variable for reuse later.
  754. $connection = tripal_db_persistent_chado();
  755. // if we cannot get a connection the abandon the prepared statement
  756. if (!$connection ) {
  757. $prepared = FALSE;
  758. unset($options['statement_name']);
  759. }
  760. }
  761. else {
  762. //print "NO STATEMENT (update): $table\n";
  763. //debug_print_backtrace();
  764. }
  765. */
  766. $delete_matches = array(); // contains the values for the where clause
  767. // get the table description
  768. $table_desc = tripal_core_get_chado_table_schema($table);
  769. $fields = $table_desc['fields'];
  770. // get the values needed for matching in the SQL statement
  771. foreach ($match as $field => $value) {
  772. if (is_array($value)) {
  773. // if the user has specified an array of values to delete rather than
  774. // FK relationships the keep those in our match
  775. if (array_values($value) === $value) {
  776. $delete_matches[$field] = $value;
  777. }
  778. else {
  779. $results = tripal_core_chado_get_foreign_key($table_desc, $field, $value);
  780. if (sizeof($results) > 1) {
  781. 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);
  782. }
  783. elseif (sizeof($results) < 1) {
  784. //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);
  785. }
  786. else {
  787. $delete_matches[$field] = $results[0];
  788. }
  789. }
  790. }
  791. else {
  792. $delete_matches[$field] = $value;
  793. }
  794. }
  795. // now build the SQL statement
  796. $sql = 'DELETE FROM {' . $table . '} WHERE ';
  797. $psql = $sql;
  798. $uargs = array();
  799. $idatatypes = array();
  800. $pvalues = array();
  801. $ivalues = array();
  802. $dargs = array();
  803. $void_prepared = 0;
  804. $i = 1;
  805. foreach ($delete_matches as $field => $value) {
  806. // if we have an array values then this is an "IN" clasue.
  807. // we cannot use prepared statements with these
  808. if (count($value) > 1) {
  809. $sql .= "$field IN (" . db_placeholders($value, 'varchar') . ") AND ";
  810. foreach ($value as $v) {
  811. $dargs[] = $v;
  812. }
  813. $void_prepared = 1;
  814. continue;
  815. }
  816. if (strcasecmp($table_desc['fields'][$field]['type'], 'serial') == 0 OR
  817. strcasecmp($table_desc['fields'][$field]['type'], 'int') == 0 OR
  818. strcasecmp($table_desc['fields'][$field]['type'], 'integer') == 0) {
  819. if (strcmp($value, '__NULL__') == 0) {
  820. $sql .= " $field = NULL AND ";
  821. $ivalues[] = 'NULL';
  822. $pvalues[] = '%s';
  823. $uargs[] = 'NULL';
  824. }
  825. else {
  826. $sql .= " $field = %d AND ";
  827. $ivalues[] = $value;
  828. $pvalues[] = '%d';
  829. $uargs[] = $value;
  830. }
  831. $idatatypes[] = 'int';
  832. }
  833. elseif (strcasecmp($table_desc['fields'][$field]['type'], 'boolean')==0) {
  834. $sql .= " $field = %s AND ";
  835. $pvalues[] = '%s';
  836. if (strcmp($value, '__NULL__')==0) {
  837. $ivalues[] = 'NULL';
  838. $uargs[] = 'NULL';
  839. }
  840. else {
  841. $ivalues[] = $value;
  842. $uargs[] = $value;
  843. }
  844. $idatatypes[] = 'bool';
  845. }
  846. elseif (strcasecmp($table_desc['fields'][$field]['type'], 'float')==0) {
  847. $sql .= " $field = %s AND ";
  848. $pvalues[] = '%s';
  849. if (strcmp($value, '__NULL__')==0) {
  850. $ivalues[] = 'NULL';
  851. $uargs[] = 'NULL';
  852. }
  853. else {
  854. $ivalues[] = $value;
  855. $uargs[] = $value;
  856. }
  857. $idatatypes[] = 'numeric';
  858. }
  859. else {
  860. if (strcmp($value, '__NULL__')==0) {
  861. $sql .= " $field = %s AND ";
  862. $ivalues[] = 'NULL';
  863. $uargs[] = 'NULL';
  864. $pvalues[] = '%s';
  865. }
  866. else {
  867. $sql .= " $field = '%s' AND ";
  868. $ivalues[] = $value;
  869. $uargs[] = $value;
  870. $pvalues[] = "'%s'";
  871. }
  872. $idatatypes[] = 'text';
  873. }
  874. array_push($dargs, $value);
  875. $psql .= "$field = \$" . $i . " AND ";
  876. $i++;
  877. }
  878. $sql = drupal_substr($sql, 0, -4); // get rid of the trailing 'AND'
  879. $psql = drupal_substr($psql, 0, -4); // get rid of the trailing 'AND'
  880. // finish constructing the prepared SQL statement
  881. // $psql = "PREPARE " . $options['statement_name'] . " (" . implode(', ', $idatatypes) . ") AS " . $psql;
  882. /*
  883. // finally perform the update. If successful, return the updated record
  884. if ($prepared and !$void_prepared) {
  885. // if this is the first time we've run this query
  886. // then we need to do the prepare, otherwise just execute
  887. if ($options['is_prepared'] != TRUE and
  888. !tripal_core_is_sql_prepared($options['statement_name'])) {
  889. $status = chado_query($psql);
  890. if (!$status) {
  891. watchdog('tripal_core', "tripal_core_chado_delete: not able to prepare '%name' statement for: %sql", array('%name' => $options['statement_name'], '%sql' => $sql), WATCHDOG_ERROR);
  892. return FALSE;
  893. }
  894. }
  895. $sql = "EXECUTE " . $options['statement_name'] . "(" . implode(", ", $pvalues) . ")";
  896. $resource = chado_query($sql, $ivalues);
  897. }
  898. // if it's not a prepared statement then insert normally
  899. else {
  900. */
  901. $resource = chado_query($sql, $uargs);
  902. // }
  903. // finally perform the delete. If successful, return the updated record
  904. $result = chado_query($sql, $dargs);
  905. if ($result) {
  906. return TRUE;
  907. }
  908. else {
  909. watchdog('tripal_core', "Cannot delete record in $table table. Match:" . print_r($match, 1) . ". Values: " . print_r($values, 1), array(), 'WATCHDOG_ERROR');
  910. return FALSE;
  911. }
  912. return FALSE;
  913. }
  914. /**
  915. * Provides a generic routine for selecting data from a Chado table
  916. *
  917. * Use this function to perform a simple select from any Chado table.
  918. *
  919. * @param $table
  920. * The name of the chado table for inserting
  921. * @param $columns
  922. * An array of column names
  923. * @param $values
  924. * An associative array containing the values for filtering the results. In the
  925. * case where multiple values for the same time are to be selected an additional
  926. * entry for the field should appear for each value
  927. * @param $options
  928. * An associative array of additional options where the key is the option
  929. * and the value is the value of that option.
  930. *
  931. * Additional Options Include:
  932. * - has_record
  933. * Set this argument to 'TRUE' to have this function return a numeric
  934. * value for the number of recrods rather than the array of records. this
  935. * can be useful in 'if' statements to check the presence of particula records.
  936. * - return_sql
  937. * Set this to 'TRUE' to have this function return an array where the first
  938. * element is the sql that would have been run and the second is an array of
  939. * arguments.
  940. * - case_insensitive_columns
  941. * An array of columns to do a case insensitive search on.
  942. * - regex_columns
  943. * An array of columns where the value passed in should be treated as a regular expression
  944. * - order_by
  945. * An associative array containing the column names of the table as keys
  946. * and the type of sort (i.e. ASC, DESC) as the values. The results in the
  947. * query will be sorted by the key values in the direction listed by the value
  948. * - statement_name: the name of the prepared statement to use. If the statement
  949. * has not yet been prepared it will be prepared automatically. On subsequent
  950. * calls with the same statement_name only an execute on the previously
  951. * prepared statement will occur.
  952. * - is_prepared: TRUE or FALSE. Whether or not the statement is prepared. By
  953. * default if the statement is not prepared it will be automatically.
  954. * However to avoid this check, which requires a database query you can
  955. * set this value to true and the check will not be performed.
  956. * - is_duplicate: TRUE or FALSE. Checks the values submited to see if
  957. * they violate any of the unique constraints. If so, the record
  958. * is returned, if not, FALSE is returned.
  959. * - pager: Use this option if it is desired to return only a subset of results
  960. * so that they may be shown with in a Drupal-style pager. This should be
  961. * an array with two keys: 'limit' and 'element'. The value of 'limit'
  962. * should specify the number of records to return and 'element' is a
  963. * unique integer to differentiate between pagers when more than one
  964. * appear on a page. The 'element' should start with zero and increment by
  965. * one for each pager. The pager currently does not work with prepared queries
  966. * (when using the -statement_name option).
  967. *
  968. * @return
  969. * A database query result resource, FALSE if the query was not executed
  970. * correctly, an empty array if no records were matched, or the number of records
  971. * in the dataset if $has_record is set.
  972. * If the option 'is_duplicate' is provided and the record is a duplicate it
  973. * will return the duplicated record. If the 'has_record' option is provided
  974. * a value of TRUE will be returned if a record exists and FALSE will bee
  975. * returned if there are not records.
  976. *
  977. * Example usage:
  978. * @code
  979. * $columns = array('feature_id', 'name');
  980. * $values = array(
  981. * 'organism_id' => array(
  982. * 'genus' => 'Citrus',
  983. * 'species' => array('sinensis', 'clementina'),
  984. * ),
  985. * 'uniquename' => 'orange1.1g000034m.g',
  986. * 'type_id' => array (
  987. * 'cv_id' => array (
  988. * 'name' => 'sequence',
  989. * ),
  990. * 'name' => 'gene',
  991. * 'is_obsolete' => 0
  992. * ),
  993. * );
  994. * $options = array(
  995. * 'statement_name' => 'sel_feature_genus_species_cvname'
  996. * 'order_by' => array(
  997. * 'name' => 'ASC'
  998. * ),
  999. * );
  1000. * $result = tripal_core_chado_select('feature',$columns,$values,$options);
  1001. * @endcode
  1002. * The above code selects a record from the feature table using the three fields
  1003. * that uniquely identify a feature. The $columns array simply lists the columns
  1004. * to select. The $values array is nested such that the organism is identified by
  1005. * way of the organism_id foreign key constraint by specifying the genus and
  1006. * species. The cvterm is also specified using its foreign key and the cv_id
  1007. * for the cvterm is nested as well. In the example above, two different species
  1008. * are allowed to match
  1009. *
  1010. * @ingroup tripal_chado_api
  1011. */
  1012. function tripal_core_chado_select($table, $columns, $values, $options = NULL) {
  1013. if (!is_array($values)) {
  1014. watchdog('tripal_core', 'Cannot pass non array as values for selecting.', array(),
  1015. WATCHDOG_ERROR);
  1016. return FALSE;
  1017. }
  1018. if (!is_array($columns)) {
  1019. watchdog('tripal_core', 'Cannot pass non array as columns for selecting.', array(),
  1020. WATCHDOG_ERROR);
  1021. return FALSE;
  1022. }
  1023. if (count($columns)==0) {
  1024. watchdog('tripal_core', 'Cannot pass an empty array as columns for selecting.', array(),
  1025. WATCHDOG_ERROR);
  1026. return FALSE;
  1027. }
  1028. // set defaults for options. If we don't set defaults then
  1029. // we get memory leaks when we try to access the elements
  1030. if (!is_array($options)) {
  1031. $options = array();
  1032. }
  1033. if (!array_key_exists('case_insensitive_columns', $options)) {
  1034. $options['case_insensitive_columns'] = array();
  1035. }
  1036. if (!array_key_exists('regex_columns', $options)) {
  1037. $options['regex_columns'] = array();
  1038. }
  1039. if (!array_key_exists('order_by', $options)) {
  1040. $options['order_by'] = array();
  1041. }
  1042. /*
  1043. if (!array_key_exists('is_prepared', $options)) {
  1044. $options['is_prepared'] = FALSE;
  1045. }
  1046. */
  1047. if (!array_key_exists('return_sql', $options)) {
  1048. $options['return_sql'] = FALSE;
  1049. }
  1050. if (!array_key_exists('has_record', $options)) {
  1051. $options['has_record'] = FALSE;
  1052. }
  1053. /*
  1054. if (!array_key_exists('statement_name', $options)) {
  1055. $options['statement_name'] = FALSE;
  1056. }
  1057. */
  1058. if (!array_key_exists('is_duplicate', $options)) {
  1059. $options['is_duplicate'] = FALSE;
  1060. }
  1061. $pager = array();
  1062. if (array_key_exists('pager', $options)) {
  1063. $pager = $options['pager'];
  1064. }
  1065. /*
  1066. // if this is a prepared statement check to see if it has already been prepared
  1067. $prepared = FALSE;
  1068. if ($options['statement_name']) {
  1069. $prepared = TRUE;
  1070. // we need to get a persistent connection. If one exists this function
  1071. // will not recreate it, but if not it will create one and store it in
  1072. // a Drupal variable for reuse later.
  1073. $connection = tripal_db_persistent_chado();
  1074. // if we cannot get a connection the abandon the prepared statement
  1075. if (!$connection) {
  1076. $prepared = FALSE;
  1077. unset($options['statement_name']);
  1078. }
  1079. }
  1080. else {
  1081. //print "NO STATEMENT (select): $table\n";
  1082. //debug_print_backtrace();
  1083. }
  1084. */
  1085. // check that our columns and values arguments are proper arrays
  1086. if (!is_array($columns)) {
  1087. watchdog('tripal_core', 'the $columns argument for tripal_core_chado_select must be an array.');
  1088. return FALSE;
  1089. }
  1090. if (!is_array($values)) {
  1091. watchdog('tripal_core', 'the $values argument for tripal_core_chado_select must be an array.');
  1092. return FALSE;
  1093. }
  1094. // get the table description
  1095. $table_desc = tripal_core_get_chado_table_schema($table);
  1096. $select = '';
  1097. $from = '';
  1098. $where = '';
  1099. $args = array();
  1100. // if the 'use_unique' option is turned on then we want
  1101. // to remove all but unique keys
  1102. if ($options['is_duplicate'] and array_key_exists('unique keys', $table_desc)) {
  1103. $ukeys = $table_desc['unique keys'];
  1104. $has_results = 0;
  1105. // iterate through the unique constraints and reset the values and columns
  1106. // arrays to only include these fields
  1107. foreach ($ukeys as $cname => $fields) {
  1108. if ($has_results) {
  1109. continue;
  1110. }
  1111. $new_values = array();
  1112. $new_columns = array();
  1113. $new_options = array();
  1114. $uq_sname = "uq_" . $table . "_";
  1115. $has_pkey = 0;
  1116. // include the primary key in the results returned
  1117. if (array_key_exists('primary key', $table_desc)) {
  1118. $has_pkey = 1;
  1119. $pkeys = $table_desc['primary key'];
  1120. foreach ($pkeys as $index => $key) {
  1121. array_push($new_columns, $key);
  1122. }
  1123. }
  1124. // recreate the $values and $columns arrays
  1125. foreach ($fields as $field) {
  1126. if (array_key_exists($field, $values)) {
  1127. $new_values[$field] = $values[$field];
  1128. $uq_sname .= substr($field, 0, 2);
  1129. // if there is no primary key then use the unique contraint fields
  1130. if (!$has_pkey) {
  1131. array_push($new_columns, $field);
  1132. }
  1133. }
  1134. // if the field doesn't exist in the values array then
  1135. // substitute any default values
  1136. elseif (array_key_exists('default', $table_desc['fields'][$field])) {
  1137. $new_values[$field] = $table_desc['fields'][$field]['default'];
  1138. $uq_sname .= substr($field, 0, 2);
  1139. if (!$has_pkey) {
  1140. array_push($new_columns, $field);
  1141. }
  1142. }
  1143. // if there is no value (default or otherwise) check if this field is
  1144. // allowed to be null
  1145. elseif (!$table_desc['fields'][$field]['not null']) {
  1146. $new_values[$field] = NULL;
  1147. $uq_sname .= "n" . substr($field, 0, 2);
  1148. if (!$has_pkey) {
  1149. array_push($new_columns, $field);
  1150. }
  1151. }
  1152. // if the array key doesn't exist in the values given by the caller
  1153. // and there is no default value then we cannot check if the record
  1154. // is a duplicate so return FALSE
  1155. else {
  1156. watchdog('tripal_core', "tripal_core_chado_select: There is no value for %field"
  1157. . " thus we cannot check if this record is unique",
  1158. array('%field' => $field), WATCHDOG_ERROR);
  1159. return FALSE;
  1160. }
  1161. }
  1162. $new_options['statement_name'] = $uq_sname;
  1163. $results = tripal_core_chado_select($table, $new_columns, $new_values, $new_options);
  1164. // if we have a duplicate record then return the results
  1165. if (count($results) > 0) {
  1166. $has_results = 1;
  1167. }
  1168. unset($new_columns);
  1169. unset($new_values);
  1170. unset($new_options);
  1171. }
  1172. if ($options['has_record'] and $has_results) {
  1173. return TRUE;
  1174. }
  1175. else {
  1176. return $results;
  1177. }
  1178. }
  1179. foreach ($values as $field => $value) {
  1180. // make sure the field is in the table description. If not then return an error
  1181. // message
  1182. if (!array_key_exists($field, $table_desc['fields'])) {
  1183. watchdog('tripal_core', "tripal_core_chado_select: The field '%field' does not exist " .
  1184. "for the table '%table'. Cannot perform query. Values: %array",
  1185. array('%field' => $field, '%table' => $table, '%array' => print_r($values, 1)), WATCHDOG_ERROR);
  1186. return array();
  1187. }
  1188. $select[] = $field;
  1189. if (is_array($value)) {
  1190. // if the user has specified multiple values for matching then this we
  1191. // want to catch that and save them in our $where array, otherwise
  1192. // we'll descend for a foreign key relationship
  1193. if (array_values($value) === $value) {
  1194. $where[$field] = $value;
  1195. }
  1196. else {
  1197. // select the value from the foreign key relationship for this value
  1198. $foreign_options = array(
  1199. 'regex_columns' => $options['regex_columns'],
  1200. // 'case_insensitive_columns' => $options['case_insensitive_columns']
  1201. );
  1202. /*
  1203. if (array_key_exists('statement_name', $options) and $options['statement_name']) {
  1204. // add the fk relationship info to the prepared statement name so that
  1205. // we can prepare the selects run by the recrusive tripal_core_chado_get_foreign_key
  1206. // function. we need the statement name to be unique so take the first two characters of each column
  1207. $fk_sname = "fk_" . $table . "_" . $field;
  1208. foreach ($value as $k => $v) {
  1209. $fk_sname .= substr($k, 0, 2);
  1210. }
  1211. $foreign_options['statement_name'] = $fk_sname;
  1212. }
  1213. */
  1214. $results = tripal_core_chado_get_foreign_key($table_desc, $field, $value, $foreign_options);
  1215. if (!$results or count($results)==0) {
  1216. return array();
  1217. }
  1218. else {
  1219. $where[$field] = $results;
  1220. }
  1221. }
  1222. }
  1223. else {
  1224. // need to catch a 0 and make int if integer field
  1225. // but we don't want to catch a NULL
  1226. if ($value === NULL) {
  1227. $where[$field] = NULL;
  1228. }
  1229. elseif ($table_desc['fields'][$field]['type'] == 'int') {
  1230. $where[$field][] = (int) $value;
  1231. }
  1232. else {
  1233. $where[$field][] = $value;
  1234. }
  1235. }
  1236. }
  1237. // now build the SQL and prepared SQL statements. We may not use
  1238. // the prepared statement if it wasn't requested in the options or if the
  1239. // argument in a where statement has multiple values.
  1240. if (empty($where)) {
  1241. // sometimes want to select everything
  1242. $sql = "SELECT " . implode(', ', $columns) . " ";
  1243. $sql .= 'FROM {' . $table . '} ';
  1244. // we don't prepare a statement if there is no where clause
  1245. $prepared = FALSE;
  1246. }
  1247. else {
  1248. $sql = "SELECT " . implode(', ', $columns) . " ";
  1249. $sql .= 'FROM {' . $table . '} ';
  1250. // if $values is empty then we want all results so no where clause
  1251. if (!empty($values)) {
  1252. $sql .= "WHERE ";
  1253. }
  1254. $i = 1;
  1255. $itypes = array();
  1256. foreach ($where as $field => $value) {
  1257. // if we have multiple values returned then we need an 'IN' statement
  1258. // in our where statement
  1259. if (count($value) > 1) {
  1260. $sql .= "$field IN (";
  1261. $index = 0;
  1262. foreach ($value as $v) {
  1263. $sql .= ':' . $field . $index . ', ';
  1264. $args[':' . $field . $index] = $v;
  1265. }
  1266. $sql = substr($sql, -2, 0); // remove trailing ', '
  1267. $sql .= ") AND ";
  1268. }
  1269. // if we have a null value then we need an IS NULL in our where statement
  1270. elseif ($value === NULL) {
  1271. $sql .= "$field IS NULL AND ";
  1272. // Need to remove one from the argument count b/c nulls don't add an argument
  1273. $i--;
  1274. }
  1275. // if we have a single value then we need an = in our where statement
  1276. else {
  1277. $operator = '=';
  1278. if (in_array($field, $options['regex_columns'])) {
  1279. $operator = '~*';
  1280. }
  1281. if (in_array($field, $options['case_insensitive_columns'])) {
  1282. $sql .= "lower($field) $operator lower(:$field) AND ";
  1283. $args[":$field"] = $value[0];
  1284. }
  1285. else {
  1286. $sql .= "$field $operator :$field AND ";
  1287. $args[":$field"] = $value[0];
  1288. }
  1289. }
  1290. $i++;
  1291. } // end foreach item in where clause
  1292. $sql = drupal_substr($sql, 0, -4); // get rid of the trailing 'AND '
  1293. } // end if (empty($where)){ } else {
  1294. // finally add any ordering of the results to the SQL statement
  1295. if (count($options['order_by']) > 0) {
  1296. $sql .= " ORDER BY ";
  1297. foreach ($options['order_by'] as $field => $dir) {
  1298. $sql .= "$field $dir, ";
  1299. }
  1300. $sql = drupal_substr($sql, 0, -2); // get rid of the trailing ', '
  1301. }
  1302. // if the caller has requested the SQL rather than the results...
  1303. // which happens in the case of wanting to use the Drupal pager, then do so
  1304. if ($options['return_sql'] == TRUE) {
  1305. return array('sql' => $sql, 'args' => $args);
  1306. }
  1307. if (array_key_exists('limit', $pager)) {
  1308. $resource = chado_pager_query($sql, $pager['limit'], $pager['element'], NULL, $args);
  1309. }
  1310. else {
  1311. $resource = chado_query($sql, $args);
  1312. }
  1313. // format results into an array
  1314. $results = array();
  1315. foreach ($resource as $r) {
  1316. $results[] = $r;
  1317. }
  1318. if ($options['has_record']) {
  1319. return count($results);
  1320. }
  1321. return $results;
  1322. }
  1323. /**
  1324. * Gets the value of a foreign key relationship
  1325. *
  1326. * This function is used by tripal_core_chado_select, tripal_core_chado_insert,
  1327. * and tripal_core_chado_update to iterate through the associate array of
  1328. * values that gets passed to each of those routines. The values array
  1329. * is nested where foreign key contraints are used to specify a value that. See
  1330. * documentation for any of those functions for further information.
  1331. *
  1332. * @param $table_desc
  1333. * A table description for the table with the foreign key relationship to be identified generated by
  1334. * hook_chado_<table name>_schema()
  1335. * @param $field
  1336. * The field in the table that is the foreign key.
  1337. * @param $values
  1338. * An associative array containing the values
  1339. * @param $options
  1340. * An associative array of additional options where the key is the option
  1341. * and the value is the value of that option. These options are passed on to tripal_core_chado_select.
  1342. *
  1343. * Additional Options Include:
  1344. * - case_insensitive_columns
  1345. * An array of columns to do a case insensitive search on.
  1346. * - regex_columns
  1347. * An array of columns where the value passed in should be treated as a regular expression
  1348. *
  1349. * @return
  1350. * A string containg the results of the foreign key lookup, or FALSE if failed.
  1351. *
  1352. * Example usage:
  1353. * @code
  1354. *
  1355. * $values = array(
  1356. * 'genus' => 'Citrus',
  1357. * 'species' => 'sinensis',
  1358. * );
  1359. * $value = tripal_core_chado_get_foreign_key('feature', 'organism_id',$values);
  1360. *
  1361. * @endcode
  1362. * The above code selects a record from the feature table using the three fields
  1363. * that uniquely identify a feature. The $columns array simply lists the columns
  1364. * to select. The $values array is nested such that the organism is identified by
  1365. * way of the organism_id foreign key constraint by specifying the genus and
  1366. * species. The cvterm is also specified using its foreign key and the cv_id
  1367. * for the cvterm is nested as well.
  1368. *
  1369. * @ingroup tripal_chado_api
  1370. */
  1371. function tripal_core_chado_get_foreign_key($table_desc, $field, $values, $options = NULL) {
  1372. // set defaults for options. If we don't set defaults then
  1373. // we get memory leaks when we try to access the elements
  1374. if (!is_array($options)) {
  1375. $options = array();
  1376. }
  1377. if (!array_key_exists('case_insensitive_columns', $options)) {
  1378. $options['case_insensitive_columns'] = array();
  1379. }
  1380. if (!array_key_exists('regex_columns', $options)) {
  1381. $options['regex_columns'] = array();
  1382. }
  1383. // get the list of foreign keys for this table description and
  1384. // iterate through those until we find the one we're looking for
  1385. $fkeys = '';
  1386. if (array_key_exists('foreign keys', $table_desc)) {
  1387. $fkeys = $table_desc['foreign keys'];
  1388. }
  1389. if ($fkeys) {
  1390. foreach ($fkeys as $name => $def) {
  1391. if (is_array($def['table'])) {
  1392. //foreign key was described 2X
  1393. $message = "The foreign key " . $name . " was defined twice. Please check modules "
  1394. . "to determine if hook_chado_schema_<version>_" . $table_desc['table'] . "() was "
  1395. . "implemented and defined this foreign key when it wasn't supposed to. Modules "
  1396. . "this hook was implemented in: " . implode(', ',
  1397. module_implements("chado_" . $table_desc['table'] . "_schema")) . ".";
  1398. watchdog('tripal_core', $message);
  1399. drupal_set_message(check_plain($message), 'error');
  1400. continue;
  1401. }
  1402. $table = $def['table'];
  1403. $columns = $def['columns'];
  1404. // iterate through the columns of the foreign key relationship
  1405. foreach ($columns as $left => $right) {
  1406. // does the left column in the relationship match our field?
  1407. if (strcmp($field, $left) == 0) {
  1408. // the column name of the foreign key matches the field we want
  1409. // so this is the right relationship. Now we want to select
  1410. $select_cols = array($right);
  1411. $result = tripal_core_chado_select($table, $select_cols, $values, $options);
  1412. $fields = array();
  1413. if ($result and count($result) > 0) {
  1414. foreach ($result as $obj) {
  1415. $fields[] = $obj->$right;
  1416. }
  1417. return $fields;
  1418. }
  1419. }
  1420. }
  1421. }
  1422. }
  1423. else {
  1424. // TODO: what do we do if we get to this point and we have a fk
  1425. // relationship expected but we don't have any definition for one in the
  1426. // table schema??
  1427. $version = tripal_core_get_chado_version(TRUE);
  1428. $message = t("There is no foreign key relationship defined for " . $field . " .
  1429. To define a foreign key relationship, determine the table this foreign
  1430. key referrs to (<foreign table>) and then implement
  1431. hook_chado_chado_schema_v<version>_<foreign table>(). See
  1432. tripal_feature_chado_v1_2_schema_feature for an example. Chado version: $version");
  1433. watchdog('tripal_core', $message);
  1434. drupal_set_message(check_plain($message), 'error');
  1435. }
  1436. return array();
  1437. }
  1438. /**
  1439. * Generates an object containing the full details of a record(s) in chado.
  1440. *
  1441. * This differs from the objects returned by tripal_core_chado_select in so far as all foreign key
  1442. * relationships have been followed meaning you have more complete details. Thus this function
  1443. * should be used whenever you need a full variable and tripal_core_chado_select should be used if
  1444. * you only case about a few columns.
  1445. *
  1446. * @param $table
  1447. * The name of the base table to generate a variable for
  1448. * @param $values
  1449. * A select values array that selects the records you want from the base table
  1450. * (this has the same form as tripal_core_chado_select)
  1451. * @param $base_options
  1452. * An array containing options for the base table. For example, an
  1453. * option of 'order_by' may be used to sort results in the base table
  1454. * if more than one are returned. The options must be compatible with
  1455. * the options accepted by the tripal_core_chado_select() function.
  1456. * Additionally, These options are available for this function:
  1457. * -return_array:
  1458. * can be provided to force the function to always return an array. Default
  1459. * behavior is to return a single record if only one record exists or to return
  1460. * an array if multiple records exist.
  1461. * - include_fk:
  1462. * an array of FK relationships to follow. By default, the
  1463. * tripal_core_chado_select function will follow all FK relationships but this
  1464. * may generate more queries then is desired slowing down this function call when
  1465. * there are lots of FK relationships to follow. Provide an array specifying the
  1466. * fields to include. For example, if expanding a property table (e.g. featureprop)
  1467. * and you want the CV and accession but do not want the DB the following
  1468. * array would work:
  1469. *
  1470. * $table_options = array(
  1471. * 'include_fk' => array(
  1472. * 'type_id' => array(
  1473. * 'cv_id' => 1,
  1474. * 'dbxref_id' => 1,
  1475. * )
  1476. * )
  1477. * );
  1478. *
  1479. * The above array will expand the 'type_id' of the property table but only
  1480. * further expand the cv_id and the dbxref_id and will go no further.
  1481. * - pager:
  1482. * Use this option if it is desired to return only a subset of results
  1483. * so that they may be shown within a Drupal-style pager. This should be
  1484. * an array with two keys: 'limit' and 'element'. The value of 'limit'
  1485. * should specify the number of records to return and 'element' is a
  1486. * unique integer to differentiate between pagers when more than one
  1487. * appear on a page. The 'element' should start with zero and increment by
  1488. * one for each pager. This only works when type is a 'table'.
  1489. * @return
  1490. * Either an object (if only one record was selected from the base table)
  1491. * or an array of objects (if more than one record was selected from the base table).
  1492. * If the option 'return_array' is provided the function always returns an array.
  1493. *
  1494. * Example Usage:
  1495. * @code
  1496. $values = array(
  1497. 'name' => 'Medtr4g030710'
  1498. );
  1499. $features = tripal_core_generate_chado_var('feature', $values);
  1500. * @endcode
  1501. * This will return an object if there is only one feature with the name Medtr4g030710 or it will
  1502. * return an array of feature objects if more than one feature has that name.
  1503. *
  1504. * Note to Module Designers: Fields can be excluded by default from these objects by implementing
  1505. * one of the following hooks:
  1506. * - hook_exclude_field_from_tablename_by_default (where tablename is the name of the table):
  1507. * This hook allows you to add fields to be excluded on a per table basis. Simply implement
  1508. * this hook to return an array of fields to be excluded. For example:
  1509. * @code
  1510. mymodule_exclude_field_from_feature_by_default() {
  1511. return array('residues' => TRUE);
  1512. }
  1513. * @endcode
  1514. * will ensure that feature.residues is ecluded from a feature object by default.
  1515. * - hook_exclude_type_by_default:
  1516. * This hook allows you to exclude fields from all tables that are of a given postgresql field
  1517. * type. Simply implement this hook to return an array of postgresql types mapped to criteria.
  1518. * Then all fields of that type where the criteria supplied returns TRUE will be excluded from
  1519. * any table. Tokens available in criteria are &gt;field_value&lt; and &gt;field_name&lt; . For example:
  1520. * @code
  1521. mymodule_exclude_type_by_default() {
  1522. return array('text' => 'length(&gt;field_value&lt; ) > 50');
  1523. }
  1524. * @endcode
  1525. * 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.
  1526. *
  1527. * @ingroup tripal_chado_api
  1528. */
  1529. function tripal_core_generate_chado_var($table, $values, $base_options = array()) {
  1530. $all = new stdClass();
  1531. $return_array = 0;
  1532. if (array_key_exists('return_array', $base_options)) {
  1533. $return_array = 1;
  1534. }
  1535. $include_fk = 0;
  1536. if (array_key_exists('include_fk', $base_options)) {
  1537. $include_fk = $base_options['include_fk'];
  1538. }
  1539. $pager = array();
  1540. if (array_key_exists('pager', $base_options)) {
  1541. $pager = $base_options['pager'];
  1542. }
  1543. // get description for the current table----------------------------------------------------------
  1544. $table_desc = tripal_core_get_chado_table_schema($table);
  1545. if (!$table_desc or count($table_desc) == 0) {
  1546. watchdog('tripal_core', "tripal_core_generate_chado_var: The table '%table' has not been defined. " .
  1547. "and cannot be expanded. If this is a custom table, please add it using the Tripal " .
  1548. "custom table interface.", array('%table' => $table), WATCHDOG_ERROR);
  1549. if ($return_array) {
  1550. return array();
  1551. }
  1552. return FALSE;
  1553. }
  1554. $table_primary_key = $table_desc['primary key'][0];
  1555. $table_columns = array_keys($table_desc['fields']);
  1556. // Expandable fields without value needed for criteria--------------------------------------------
  1557. $all->expandable_fields = array();
  1558. if ($table_desc['referring_tables']) {
  1559. $all->expandable_tables = $table_desc['referring_tables'];
  1560. }
  1561. else {
  1562. $all->expandable_tables = array();
  1563. }
  1564. $all->expandable_nodes = array();
  1565. // Get fields to be removed by name.................................
  1566. $fields_to_remove = module_invoke_all('exclude_field_from_' . $table . '_by_default');
  1567. foreach ($fields_to_remove as $field_name => $criteria) {
  1568. //replace &gt;field_name&lt; with the current field name &
  1569. $criteria = preg_replace('/&gt;field_name&lt; /', addslashes($field_name), $criteria);
  1570. // if field_value needed we can't deal with this field yet
  1571. if (preg_match('/&gt;field_value&lt; /', $criteria)) {
  1572. break;
  1573. }
  1574. //if criteria then remove from query
  1575. // @coder-ignore: only module designers can populate $criteria -not security risk
  1576. $success = php_eval('<?php return ' . $criteria . '; ?>');
  1577. // watchdog('tripal_core',
  1578. // 'Evaluating criteria (%criteria) for field %field in tripal_core_generate_chado_var for %table evaluated to %success',
  1579. // array('%table' => $table, '%criteria'=>$criteria, '%field' => $field_name, '%success'=>$success),
  1580. // WATCHDOG_NOTICE
  1581. // );
  1582. if ($success) {
  1583. unset($table_columns[array_search($field_name, $table_columns)]);
  1584. unset($fields_to_remove[$field_name]);
  1585. $all->expandable_fields[] = $table . '.' . $field_name;
  1586. }
  1587. }
  1588. //Get fields to be removed by type................................
  1589. $types_to_remove = module_invoke_all('exclude_type_by_default');
  1590. $field_types = array();
  1591. foreach ($table_desc['fields'] as $field_name => $field_array) {
  1592. $field_types[$field_array['type']][] = $field_name;
  1593. }
  1594. foreach ($types_to_remove as $field_type => $criteria) {
  1595. // if there are fields of that type to remove
  1596. if (is_array($field_types[$field_type])) {
  1597. //replace &gt;field_name&lt; with the current field name &
  1598. $criteria = preg_replace('/&gt;field_name&lt; /', addslashes($field_name), $criteria);
  1599. foreach ($field_types[$field_type] as $field_name) {
  1600. // if field_value needed we can't deal with this field yet
  1601. if (preg_match('/&gt;field_value&lt; /', $criteria)) {
  1602. $fields_to_remove[$field_name] = $criteria;
  1603. continue;
  1604. }
  1605. // if field_value needed we can't deal with this field yet
  1606. if (preg_match('/&gt;field_value&lt; /', $criteria)) {
  1607. break;
  1608. }
  1609. //if criteria then remove from query
  1610. // @coder-ignore: only module designers can populate $criteria -not security risk
  1611. $success = php_eval('<?php return ' . $criteria . '; ?>');
  1612. // watchdog('tripal_core',
  1613. // 'Evaluating criteria (%criteria) for field %field of $type in tripal_core_generate_chado_var for %table evaluated to %success',
  1614. // array('%table'=>$table, '%criteria'=>$criteria, '%field'=>$field_name, '%type'=>$field_type, '%success'=>$success),
  1615. // WATCHDOG_NOTICE
  1616. // );
  1617. if ($success) {
  1618. unset($table_columns[array_search($field_name, $table_columns)]);
  1619. $all->expandable_fields[] = $table . '.' . $field_name;
  1620. }
  1621. } //end of foreach field of that type
  1622. }
  1623. } //end of foreach type to be removed
  1624. // get the values for the record in the current table---------------------------------------------
  1625. $results = tripal_core_chado_select($table, $table_columns, $values, $base_options);
  1626. if ($results) {
  1627. foreach ($results as $key => $object) {
  1628. // Add empty expandable_x arrays
  1629. $object->expandable_fields = $all->expandable_fields;
  1630. $object->expandable_tables = $all->expandable_tables;
  1631. $object->expandable_nodes = $all->expandable_nodes;
  1632. // add curent table
  1633. $object->tablename = $table;
  1634. // check if the current table maps to a node type-----------------------------------------------
  1635. // if this table is connected to a node there will be a chado_tablename table in drupal
  1636. if (db_table_exists('chado_' . $table)) {
  1637. // that has a foreign key to this one ($table_desc['primary key'][0]
  1638. // and to the node table (nid)
  1639. $sql = "
  1640. SELECT $table_primary_key, nid
  1641. FROM {chado_$table}
  1642. WHERE $table_primary_key = :$table_primary_key";
  1643. $results = db_query($sql, array(":$table_primary_key" . $object->{$table_primary_key}));
  1644. $mapping = $results->fetchObject();
  1645. if ($mapping->{$table_primary_key}) {
  1646. $object->nid = $mapping->nid;
  1647. $object->expandable_nodes[] = $table;
  1648. }
  1649. }
  1650. // remove any fields where criteria need to be evalulated---------------------------------------
  1651. foreach ($fields_to_remove as $field_name => $criteria) {
  1652. if (!isset($object->{$field_name})) {
  1653. break;
  1654. }
  1655. $criteria = preg_replace('/&gt;field_value&lt; /', addslashes($object->{$field_name}), $criteria);
  1656. //if criteria then remove from query
  1657. // @coder-ignore: only module designers can populate $criteria -not security risk
  1658. $success = php_eval('<?php return ' . $criteria . '; ?>');
  1659. // watchdog('tripal_core',
  1660. // 'Evaluating criteria (%criteria) for field %field in tripal_core_generate_chado_var for %table evaluated to %success',
  1661. // array('%table' => $table, '%criteria'=>$criteria, '%field' => $field_name, '%success'=>$success),
  1662. // WATCHDOG_NOTICE
  1663. // );
  1664. if ($success) {
  1665. unset($object->{$field_name});
  1666. $object->expandable_fields[] = $table . '.' . $field_name;
  1667. }
  1668. }
  1669. // recursively follow foreign key relationships nesting objects as we go------------------------
  1670. if ($table_desc['foreign keys']) {
  1671. foreach ($table_desc['foreign keys'] as $foreign_key_array) {
  1672. $foreign_table = $foreign_key_array['table'];
  1673. foreach ($foreign_key_array['columns'] as $foreign_key => $primary_key) {
  1674. // Note: Foreign key is the field in the current table whereas primary_key is the field in
  1675. // the table referenced by the foreign key
  1676. //Dont do anything if the foreign key is empty
  1677. if (empty($object->{$foreign_key})) {
  1678. continue;
  1679. }
  1680. if ($include_fk) {
  1681. // don't recurse if the callee has supplied an $fk_include list and this
  1682. // FK table is not in the list.
  1683. if (is_array($include_fk) and !array_key_exists($foreign_key, $include_fk)) {
  1684. continue;
  1685. }
  1686. // if we have the option but it is not an array then we don't recurse any furutehr
  1687. if (!is_array($include_fk)) {
  1688. continue;
  1689. }
  1690. }
  1691. // get the record from the foreign table
  1692. $foreign_values = array($primary_key => $object->{$foreign_key});
  1693. $options = array();
  1694. if (is_array($include_fk)) {
  1695. $options['include_fk'] = $include_fk[$foreign_key];
  1696. }
  1697. $foreign_object = tripal_core_generate_chado_var($foreign_table, $foreign_values, $options);
  1698. // add the foreign record to the current object in a nested manner
  1699. $object->{$foreign_key} = $foreign_object;
  1700. // Flatten expandable_x arrays so only in the bottom object
  1701. if (is_array($object->{$foreign_key}->expandable_fields)) {
  1702. $object->expandable_fields = array_merge(
  1703. $object->expandable_fields,
  1704. $object->{$foreign_key}->expandable_fields
  1705. );
  1706. unset($object->{$foreign_key}->expandable_fields);
  1707. }
  1708. if (is_array($object->{$foreign_key}->expandable_tables)) {
  1709. $object->expandable_tables = array_merge(
  1710. $object->expandable_tables,
  1711. $object->{$foreign_key}->expandable_tables
  1712. );
  1713. unset($object->{$foreign_key}->expandable_tables);
  1714. }
  1715. if (is_array($object->{$foreign_key}->expandable_nodes)) {
  1716. $object->expandable_nodes = array_merge(
  1717. $object->expandable_nodes,
  1718. $object->{$foreign_key}->expandable_nodes
  1719. );
  1720. unset($object->{$foreign_key}->expandable_nodes);
  1721. }
  1722. }
  1723. }
  1724. $results[$key] = $object;
  1725. }
  1726. }
  1727. }
  1728. // check only one result returned
  1729. if (!$return_array) {
  1730. if (sizeof($results) == 1) {
  1731. // add results to object
  1732. return $results[0];
  1733. }
  1734. elseif (!empty($results)) {
  1735. return $results;
  1736. }
  1737. else {
  1738. // no results returned
  1739. }
  1740. }
  1741. // the caller has requested results are always returned as
  1742. // an array
  1743. else {
  1744. if (!$results) {
  1745. return array();
  1746. }
  1747. else {
  1748. return $results;
  1749. }
  1750. }
  1751. }
  1752. /**
  1753. * Retrieves fields/tables/nodes that were excluded by default from a variable and adds them
  1754. *
  1755. * This function exists to allow tripal_core_generate_chado_var() to excldue some
  1756. * fields/tables/nodes from the default form of a variable without making it extremely difficult for
  1757. * the tripal admin to get at these variables if he/she wants them.
  1758. *
  1759. * @param $object
  1760. * This must be an object generated using tripal_core_generate_chado_var()
  1761. * @param $type
  1762. * Must be one of 'field', 'table', 'node'. Indicates what is being expanded.
  1763. * @param $to_expand
  1764. * The name of the field/table/node to be expanded
  1765. * @param $table_options
  1766. * - order_by:
  1767. * An array containing options for the base table. For example, an
  1768. * option of 'order_by' may be used to sort results in the base table
  1769. * if more than one are returned. The options must be compatible with
  1770. * the options accepted by the tripal_core_chado_select() function.
  1771. * - return_array:
  1772. * Additionally, The option 'return_array' can be provided to force
  1773. * the function to expand tables as an array. Default behavior is to expand
  1774. * a table as single record if only one record exists or to expand as an array if
  1775. * multiple records exist.
  1776. * - include_fk:
  1777. * an array of FK relationships to follow. By default, the
  1778. * tripal_core_chado_select function will follow all FK relationships but this
  1779. * may generate more queries then is desired slowing down this function call when
  1780. * there are lots of FK relationships to follow. Provide an array specifying the
  1781. * fields to include. For example, if expanding a property table (e.g. featureprop)
  1782. * and you want the CV and accession but do not want the DB the following
  1783. * array would work:
  1784. * $table_options = array(
  1785. * 'include_fk' => array(
  1786. * 'type_id' => array(
  1787. * 'cv_id' => 1,
  1788. * 'dbxref_id' => 1,
  1789. * )
  1790. * )
  1791. * );
  1792. *
  1793. * The above array will expand the 'type_id' of the property table but only
  1794. * further expand the cv_id and the dbxref_id and will go no further.
  1795. * - pager:
  1796. * Use this option if it is desired to return only a subset of results
  1797. * so that they may be shown within a Drupal-style pager. This should be
  1798. * an array with two keys: 'limit' and 'element'. The value of 'limit'
  1799. * should specify the number of records to return and 'element' is a
  1800. * unique integer to differentiate between pagers when more than one
  1801. * appear on a page. The 'element' should start with zero and increment by
  1802. * one for each pager. This only works when type is a 'table'.
  1803. * @return
  1804. * A chado object supplemented with the field/table/node requested to be expanded.
  1805. * If the type is a table and it has already been expanded no changes is made to the
  1806. * returned object
  1807. *
  1808. * Example Usage:
  1809. * @code
  1810. // Get a chado object to be expanded
  1811. $values = array(
  1812. 'name' => 'Medtr4g030710'
  1813. );
  1814. $features = tripal_core_generate_chado_var('feature', $values);
  1815. // Expand the organism node
  1816. $feature = tripal_core_expand_chado_vars($feature, 'node', 'organism');
  1817. // Expand the feature.residues field
  1818. $feature = tripal_core_expand_chado_vars($feature, 'field', 'feature.residues');
  1819. // Expand the feature properties (featureprop table)
  1820. $feature = tripal_core_expand_chado_vars($feature, 'table', 'featureprop');
  1821. * @endcode
  1822. *
  1823. * @ingroup tripal_chado_api
  1824. */
  1825. function tripal_core_expand_chado_vars($object, $type, $to_expand, $table_options = array()) {
  1826. // make sure we have a value
  1827. if (!$object) {
  1828. watchdog('tripal_core', 'Cannot pass non array as arugment, $object, to tripal_core_expand_chado_vars function.', array(), WATCHDOG_ERROR);
  1829. return $object;
  1830. }
  1831. // check to see if we are expanding an array of objects
  1832. if (is_array($object)) {
  1833. foreach ($object as $index => $o) {
  1834. $object[$index] = tripal_core_expand_chado_vars($o, $type, $to_expand);
  1835. }
  1836. return $object;
  1837. }
  1838. // get the base table name
  1839. $base_table = $object->tablename;
  1840. switch ($type) {
  1841. case "field": //--------------------------------------------------------------------------------
  1842. if (preg_match('/(\w+)\.(\w+)/', $to_expand, $matches)) {
  1843. $tablename = $matches[1];
  1844. $fieldname = $matches[2];
  1845. $table_desc = tripal_core_get_chado_table_schema($tablename);
  1846. $values = array();
  1847. foreach ($table_desc['primary key'] as $key) {
  1848. $values[$key] = $object->{$key};
  1849. }
  1850. if ($base_table == $tablename) {
  1851. //get the field
  1852. $results = tripal_core_chado_select($tablename, array($fieldname), $values);
  1853. $object->{$fieldname} = $results[0]->{$fieldname};
  1854. $object->expanded = $to_expand;
  1855. }
  1856. else {
  1857. //We need to recurse -the field is in a nested object
  1858. foreach ((array) $object as $field_name => $field_value) {
  1859. if (is_object($field_value)) {
  1860. $object->{$field_name} = tripal_core_expand_chado_vars(
  1861. $field_value,
  1862. 'field',
  1863. $to_expand
  1864. );
  1865. }
  1866. } //end of for each field in the current object
  1867. }
  1868. }
  1869. else {
  1870. watchdog('tripal_core', 'tripal_core_expand_chado_vars: Field (%field) not in the right format. " .
  1871. "It should be <tablename>.<fieldname>', WATCHDOG_ERROR);
  1872. }
  1873. break;
  1874. case "table": //--------------------------------------------------------------------------------
  1875. $foreign_table = $to_expand;
  1876. // don't expand the table it already is expanded
  1877. if (array_key_exists($foreign_table, $object)) {
  1878. return $object;
  1879. }
  1880. $foreign_table_desc = tripal_core_get_chado_table_schema($foreign_table);
  1881. // If it's connected to the base table via a FK constraint
  1882. if ($foreign_table_desc['foreign keys'][$base_table]) {
  1883. foreach ($foreign_table_desc['foreign keys'][$base_table]['columns'] as $left => $right) {
  1884. // if the FK value in the base table is not there then we can't expand it, so just skip it.
  1885. if (!$object->{$right}) {
  1886. continue;
  1887. }
  1888. // generate a new object for this table using the FK values in the base table.
  1889. // if a prepared statement is provided generate a new statement_name so that
  1890. // we don't conflict when we recurse.
  1891. $new_options = $table_options;
  1892. /*
  1893. if (array_key_exists('statement_name', $table_options)) {
  1894. $new_options['statement_name'] = "exp_" . $foreign_table . "_" . substr($left, 0, 2) . substr($right, 0, 2);
  1895. }
  1896. */
  1897. $foreign_object = tripal_core_generate_chado_var($foreign_table, array($left => $object->{$right}), $new_options);
  1898. // if the generation of the object was successful, update the base object to include it.
  1899. if ($foreign_object) {
  1900. // in the case where the foreign key relationships exists more
  1901. // than once with the same table we want to alter the array structure. rather than
  1902. // add the object with a key of the table name, we will add the FK field name in between
  1903. if (count($foreign_table_desc['foreign keys'][$base_table]['columns']) > 1) {
  1904. if (!is_object($object->{$foreign_table})) {
  1905. $object->{$foreign_table} = new stdClass();
  1906. }
  1907. $object->{$foreign_table}->{$left} = $foreign_object;
  1908. $object->expanded = $to_expand;
  1909. }
  1910. else {
  1911. $object->{$foreign_table} = $foreign_object;
  1912. $object->expanded = $to_expand;
  1913. }
  1914. }
  1915. // if the object returned is NULL then handle that
  1916. else {
  1917. if (count($foreign_table_desc['foreign keys'][$base_table]['columns']) > 1) {
  1918. $object->{$foreign_table}->{$left} = NULL;
  1919. }
  1920. else {
  1921. $object->{$foreign_table} = NULL;
  1922. }
  1923. }
  1924. }
  1925. }
  1926. // if the foreign table is not connected to the base table through a FK constraint
  1927. else {
  1928. // We need to recurse -the table has a relationship to one of the nested objects
  1929. $did_expansion = 0;
  1930. foreach ((array) $object as $field_name => $field_value) {
  1931. // if we have a nested object ->expand the table in it
  1932. if (is_object($field_value)) {
  1933. $did_expansion = 1;
  1934. $object->{$field_name} = tripal_core_expand_chado_vars($field_value, 'table', $foreign_table);
  1935. }
  1936. }
  1937. // if we did not expand this table we should return a message that the foreign table
  1938. // could not be expanded
  1939. if (!$did_expansion) {
  1940. watchdog('tripal_core', 'tripal_core_expand_chado_vars: Could not expand table, %table. It is ',
  1941. 'not in a foreign key relationship with the base object nor with any other expanded table. ' .
  1942. 'Check the table definition to ensure that a proper foreign key relationship is present.',
  1943. array('%table' => $foreign_table), WATCHDOG_ERROR);
  1944. }
  1945. }
  1946. break;
  1947. case "node": //---------------------------------------------------------------------------------
  1948. //if the node to be expanded is for our base table, then just expand it
  1949. if ($object->tablename == $to_expand) {
  1950. $node = node_load($object->nid);
  1951. if ($node) {
  1952. $object->expanded = $to_expand;
  1953. $node->expandable_fields = $object->expandable_fields;
  1954. unset($object->expandable_fields);
  1955. $node->expandable_tables = $object->expandable_tables;
  1956. unset($object->expandable_tables);
  1957. $node->expandable_nodes = $object->expandable_nodes;
  1958. unset($object->expandable_nodes);
  1959. $node->{$base_table} = $object;
  1960. $object = $node;
  1961. }
  1962. else {
  1963. watchdog('tripal_core', 'tripal_core_expand_chado_vars: No node matches the nid (%nid) supplied.',
  1964. array('%nid' => $object->nid), WATCHDOG_ERROR);
  1965. } //end of if node
  1966. }
  1967. else {
  1968. //We need to recurse -the node to expand is one of the nested objects
  1969. foreach ((array) $object as $field_name => $field_value) {
  1970. if (is_object($field_value)) {
  1971. $object->{$field_name} = tripal_core_expand_chado_vars(
  1972. $field_value,
  1973. 'node',
  1974. $to_expand
  1975. );
  1976. }
  1977. } //end of for each field in the current object
  1978. }
  1979. break;
  1980. default:
  1981. watchdog('tripal_core', 'tripal_core_expand_chado_vars: Unrecognized type (%type). Should be one of "field", "table", "node".',
  1982. array('%type' => $type), WATCHDOG_ERROR);
  1983. return FALSE;
  1984. }
  1985. //move extended array downwards-------------------------------------------------------------------
  1986. if (!$object->expanded) {
  1987. //if there's no extended field then go hunting for it
  1988. foreach ( (array)$object as $field_name => $field_value) {
  1989. if (is_object($field_value)) {
  1990. if (isset($field_value->expanded)) {
  1991. $object->expanded = $field_value->expanded;
  1992. unset($field_value->expanded);
  1993. }
  1994. }
  1995. }
  1996. }
  1997. //try again becasue now we might have moved it down
  1998. if ($object->expanded) {
  1999. $expandable_name = 'expandable_' . $type . 's';
  2000. if ($object->{$expandable_name}) {
  2001. $key_to_remove = array_search($object->expanded, $object->{$expandable_name});
  2002. unset($object->{$expandable_name}[$key_to_remove]);
  2003. unset($object->expanded);
  2004. }
  2005. else {
  2006. // if there is an expandable array then we've reached the base object
  2007. // if we get here and don't have anything expanded then something went wrong
  2008. // watchdog(
  2009. // 'tripal_core',
  2010. // 'tripal_core_expand_chado_vars: Unable to expand the %type %to_expand',
  2011. // array('%type'=>$type, '%to_expand'=>$to_expand),
  2012. // WATCHDOG_ERROR
  2013. // );
  2014. } //end of it we've reached the base object
  2015. }
  2016. return $object;
  2017. }
  2018. /**
  2019. * Implements hook_exclude_type_by_default()
  2020. *
  2021. * This hooks allows fields of a specified type that match a specified criteria to be excluded by
  2022. * default from any table when tripal_core_generate_chado_var() is called. Keep in mind that if
  2023. * fields are excluded by default they can always be expanded at a later date using
  2024. * tripal_core_expand_chado_vars().
  2025. *
  2026. * Criteria are php strings that evaluate to either TRUE or FALSE. These strings are evaluated using
  2027. * drupal_eval() which suppresses syntax errors and throws watchdog entries of type php. There are
  2028. * also watchdog entries of type tripal_core stating the exact criteria evaluated. Criteria can
  2029. * contain the following tokens:
  2030. * - &gt;field_name&lt;
  2031. * Replaced by the name of the field to be excluded
  2032. * - &gt;field_value&lt;
  2033. * Replaced by the value of the field in the current record
  2034. * Also keep in mind that if your criteria doesn't contain the &gt;field_value&lt; token then it will be
  2035. * evaluated before the query is executed and if the field is excluded it won't be included in the
  2036. * query.
  2037. *
  2038. * @return
  2039. * An array of type => criteria where the type is excluded if the criteria evaluates to TRUE
  2040. *
  2041. * @ingroup tripal_chado_api
  2042. */
  2043. function tripal_core_exclude_type_by_default() {
  2044. return array('text' => 'strlen("&gt;field_value&lt; ") > 100');
  2045. }
  2046. /**
  2047. * Implements hook_exclude_field_from_<tablename>_by_default()
  2048. *
  2049. * This hooks allows fields from a specified table that match a specified criteria to be excluded by
  2050. * default from any table when tripal_core_generate_chado_var() is called. Keep in mind that if
  2051. * fields are excluded by default they can always be expanded at a later date using
  2052. * tripal_core_expand_chado_vars().
  2053. *
  2054. * Criteria are php strings that evaluate to either TRUE or FALSE. These strings are evaluated using
  2055. * drupal_eval() which suppresses syntax errors and throws watchdog entries of type php. There are
  2056. * also watchdog entries of type tripal_core stating the exact criteria evaluated. Criteria can
  2057. * contain the following tokens:
  2058. * - &gt;field_name&lt;
  2059. * Replaced by the name of the field to be excluded
  2060. * - &gt;field_value&lt;
  2061. * Replaced by the value of the field in the current record
  2062. * Also keep in mind that if your criteria doesn't contain the &gt;field_value&lt; token then it will be
  2063. * evaluated before the query is executed and if the field is excluded it won't be included in the
  2064. * query.
  2065. *
  2066. * @return
  2067. * An array of type => criteria where the type is excluded if the criteria evaluates to TRUE
  2068. *
  2069. * @ingroup tripal_chado_api
  2070. */
  2071. function tripal_core_exclude_field_from_feature_by_default() {
  2072. return array();
  2073. }
  2074. /**
  2075. * Use this function instead of pager_query() when selecting a
  2076. * subset of records from a Chado table.
  2077. *
  2078. * @param $query
  2079. * The SQL statement to execute, this is followed by a variable number of args
  2080. * used as substitution values in the SQL statement.
  2081. * @param $limit
  2082. * The number of query results to display per page.
  2083. * @param $element
  2084. * An optional integer to distinguish between multiple pagers on one page.
  2085. * @param $count_query
  2086. * An SQL query used to count matching records.
  2087. *
  2088. * @returns
  2089. * A database query result resource or FALSE if the query was not
  2090. * executed correctly
  2091. *
  2092. * @ingroup tripal_chado_api
  2093. */
  2094. function chado_pager_query($query, $limit, $element, $count_query) {
  2095. // The following code is almost an exact duplicate of the
  2096. // Drupal pager_query function. However, substitions have
  2097. // been made to call chado_query rather than db_query
  2098. global $pager_page_array, $pager_total, $pager_total_items;
  2099. $page = isset($_GET['page']) ? $_GET['page'] : '';
  2100. // get the SQL query arguments that get substituted into modifiers later.
  2101. $args = func_get_args();
  2102. $args = array_slice($args, 4);
  2103. // Alternative syntax for '...'
  2104. if (isset($args[0]) && is_array($args[0])) {
  2105. $args = $args[0];
  2106. }
  2107. // Construct a count query if none was given.
  2108. if (!isset($count_query)) {
  2109. $count_query = preg_replace(array('/SELECT.*?FROM /As', '/ORDER BY .*/'), array('SELECT COUNT(*) FROM ', ''), $query);
  2110. }
  2111. // Convert comma-separated $page to an array, used by other functions.
  2112. $pager_page_array = explode(',', $page);
  2113. // We calculate the total of pages as ceil(items / limit).
  2114. $pager_total_items[$element] = db_result(chado_query($count_query, $args));
  2115. $pager_total[$element] = ceil($pager_total_items[$element] / $limit);
  2116. $pager_page_array[$element] = max(0, min((int) $pager_page_array[$element], ((int) $pager_total[$element]) - 1));
  2117. return chado_query_range($query, $args, $pager_page_array[$element] * $limit, $limit);
  2118. }
  2119. /**
  2120. * Use this function instead of db_query_range().
  2121. *
  2122. * @param $sql
  2123. * The SQL statement to execute, this is followed by a variable number of args
  2124. * used as substitution values in the SQL statement.
  2125. * @param $args
  2126. * The SQL arguments
  2127. * @param $from
  2128. * The first result row to return..
  2129. * @param $count
  2130. * The maximum number of result rows to return.
  2131. *
  2132. * @returns
  2133. * A database query result resource or FALSE if the query was not
  2134. * executed correctly
  2135. *
  2136. * @ingroup tripal_chado_api
  2137. */
  2138. function chado_query_range($query, $args, $from, $count) {
  2139. $query .= ' LIMIT ' . (int) $count . ' OFFSET ' . (int) $from;
  2140. return chado_query($query, $args);
  2141. }
  2142. /**
  2143. * Use this function instead of db_query() to avoid switching databases
  2144. * when making query to the chado database
  2145. *
  2146. * Will use a chado persistent connection if it already exists
  2147. *
  2148. * @param $sql
  2149. * The sql statement to execute
  2150. *
  2151. * @param $args
  2152. * The array of arguments, with the same structure as passed to
  2153. * the db_query() function of Drupal.
  2154. *
  2155. * @return
  2156. * DatabaseStatementInterface A prepared statement object, already executed.
  2157. *
  2158. * @ingroup tripal_chado_api
  2159. */
  2160. function chado_query($sql, $args = array()) {
  2161. $is_local = tripal_core_is_chado_local();
  2162. // if Chado is local to the database then prefix the Chado table
  2163. // names with 'chado'.
  2164. if ($is_local) {
  2165. $sql = preg_replace('/\n/', '', $sql); // remove carriage returns
  2166. $sql = preg_replace('/\{(.*?)\}/', 'chado.$1', $sql);
  2167. $results = db_query($sql, $args);
  2168. }
  2169. // if Chado is not local to the Drupal database then we have to
  2170. // switch to another database
  2171. else {
  2172. $previous_db = tripal_db_set_active('chado') ;
  2173. $results = db_query($sql);
  2174. tripal_db_set_active($previous_db);
  2175. }
  2176. return $results;
  2177. }
  2178. /**
  2179. * Get chado id for a node. E.g, if you want to get 'analysis_id' from the
  2180. * 'analysis' table for a synced 'chado_analysis' node, use:
  2181. * $analysis_id = chado_get_id_for_node ('analysis', $node)
  2182. * Likewise,
  2183. * $organism_id = chado_get_id_for_node ('organism', $node)
  2184. * $feature_id = chado_get_id_for_node ('feature', $node)
  2185. *
  2186. * @ingroup tripal_chado_api
  2187. */
  2188. function chado_get_id_for_node($table, $node) {
  2189. $sql = "SELECT " . $table . "_id FROM {chado_$table} WHERE nid = :nid";
  2190. $results = db_query($sql, array(':nid' => $node->nid));
  2191. return $results->fetchObject();
  2192. }
  2193. /**
  2194. * Get node id for a chado feature/organism/analysis. E.g, if you want to
  2195. * get the node id for an analysis, use:
  2196. * $nid = chado_get_node_id ('analysis', $analysis_id)
  2197. * Likewise,
  2198. * $nid = chado_get_node_id ('organism', $organism_id)
  2199. * $nid = chado_get_node_id ('feature', $feature_id)
  2200. *
  2201. * @ingroup tripal_chado_api
  2202. */
  2203. function chado_get_node_id($table, $id) {
  2204. $sql = "SELECT nid FROM {chado_$table} WHERE " . $table . "_id = :" . $table . "_id";
  2205. $results = db_query($sql, array(":" . $table . "_id" => $id));
  2206. return $results->fetchObject();
  2207. }
  2208. /**
  2209. * Retrieve a property for a given base table record
  2210. *
  2211. * @param $basetable
  2212. * The base table for which the property should be retrieved. Thus to retrieve a property
  2213. * for a feature the basetable=feature and property is retrieved from featureprop
  2214. * @param $record_id
  2215. * The foriegn key field of the base table. This should be in integer.
  2216. * @param $property
  2217. * The cvterm name describing the type of properties to be retrieved
  2218. * @param $cv_name
  2219. * The name of the cv that the above cvterm is part of
  2220. *
  2221. * @return
  2222. * An array in the same format as that generated by the function
  2223. * tripal_core_generate_chado_var(). If only one record is returned it
  2224. * is a single object. If more than one record is returned then it is an array
  2225. * of objects
  2226. *
  2227. * @ingroup tripal_chado_api
  2228. */
  2229. function tripal_core_get_property($basetable, $record_id, $property, $cv_name) {
  2230. // get the foreign key for this property table
  2231. $table_desc = tripal_core_get_chado_table_schema($basetable . 'prop');
  2232. $fkcol = key($table_desc['foreign keys'][$basetable]['columns']);
  2233. // construct the array of values to be selected
  2234. $values = array(
  2235. $fkcol => $record_id,
  2236. 'type_id' => array(
  2237. 'cv_id' => array(
  2238. 'name' => $cv_name,
  2239. ),
  2240. 'name' => $property,
  2241. 'is_obsolete' => 0
  2242. ),
  2243. );
  2244. $results = tripal_core_generate_chado_var($basetable . 'prop', $values);
  2245. if ($results) {
  2246. $results = tripal_core_expand_chado_vars($results, 'field', $basetable . 'prop.value');
  2247. }
  2248. return $results;
  2249. }
  2250. /**
  2251. * Insert a property for a given base table. By default if the property already
  2252. * exists a new property is added with the next available rank. If
  2253. * $update_if_present argument is specified then the record will be updated if it
  2254. * exists rather than adding a new property.
  2255. *
  2256. * @param $basetable
  2257. * The base table for which the property should be inserted. Thus to insert a property
  2258. * for a feature the basetable=feature and property is inserted into featureprop
  2259. * @param $record_id
  2260. * The foriegn key value of the base table. This should be in integer.
  2261. * @param $property
  2262. * The cvterm name describing the type of properties to be inserted
  2263. * @param $cv_name
  2264. * The name of the cv that the above cvterm is part of
  2265. * @param $value
  2266. * The value of the property to be inserted (can be empty)
  2267. * @param $update_if_present
  2268. * A boolean indicating whether an existing record should be updated. If the
  2269. * property already exists and this value is not specified or is zero then
  2270. * a new property will be added with the next largest rank.
  2271. *
  2272. * @return
  2273. * Return True on Insert/Update and False otherwise
  2274. *
  2275. * @ingroup tripal_chado_api
  2276. */
  2277. function tripal_core_insert_property($basetable, $record_id, $property,
  2278. $cv_name, $value, $update_if_present = 0) {
  2279. // first see if the property already exists, if the user want's to update
  2280. // then we can do that, but otherwise we want to increment the rank and
  2281. // insert
  2282. $props = tripal_core_get_property($basetable, $record_id, $property, $cv_name);
  2283. if (!is_array($props) and $props) {
  2284. $props = array($props);
  2285. }
  2286. $rank = 0;
  2287. if (count($props) > 0) {
  2288. if ($update_if_present) {
  2289. return tripal_core_update_property($basetable, $record_id, $property, $cv_name, $value);
  2290. }
  2291. else {
  2292. // iterate through the properties returned and check to see if the
  2293. // property with this value already exists if not, get the largest rank
  2294. // and insert the same property but with this new value
  2295. foreach ($props as $p) {
  2296. if ($p->rank > $rank) {
  2297. $rank = $p->rank;
  2298. }
  2299. if (strcmp($p->value, $value) == 0) {
  2300. return TRUE;
  2301. }
  2302. }
  2303. // now add 1 to the rank
  2304. $rank++;
  2305. }
  2306. }
  2307. // make sure the cvterm exists. Otherwise we'll get an error with
  2308. // prepared statements not matching
  2309. $values = array(
  2310. 'cv_id' => array(
  2311. 'name' => $cv_name,
  2312. ),
  2313. 'name' => $property,
  2314. );
  2315. $options = array('statement_name' => 'sel_cvterm_cv');
  2316. $term = tripal_core_chado_select('cvterm', array('cvterm_id'), $values, $options);
  2317. if (!$term or count($term) == 0) {
  2318. watchdog('tripal_core', "Cannot find property '%prop_name'.",
  2319. array('%prop_name' => $property), WATCHDOG_ERROR);
  2320. return FALSE;
  2321. }
  2322. // get the foreign key for this property table
  2323. $table_desc = tripal_core_get_chado_table_schema($basetable . 'prop');
  2324. $fkcol = key($table_desc['foreign keys'][$basetable]['columns']);
  2325. // construct the array of values to be inserted
  2326. $values = array(
  2327. $fkcol => $record_id,
  2328. 'type_id' => array(
  2329. 'cv_id' => array(
  2330. 'name' => $cv_name,
  2331. ),
  2332. 'name' => $property,
  2333. ),
  2334. 'value' => $value,
  2335. 'rank' => $rank,
  2336. );
  2337. $options = array('statement_name' => 'ins_' . $basetable . 'prop_' . substr($fkcol, 0, 2) . 'tyvara');
  2338. $result = tripal_core_chado_insert($basetable . 'prop', $values, $options);
  2339. return $result;
  2340. }
  2341. /**
  2342. * Update a property for a given base table record and property name. This
  2343. * function should be used only if one record of the property will be present.
  2344. * If the property name can have multiple entries (with increasing rank) then
  2345. * use the function named tripal_core_update_property_by_id
  2346. *
  2347. * @param $basetable
  2348. * The base table for which the property should be updated. The property table
  2349. * is constructed using a combination of the base table name and the suffix
  2350. * 'prop' (e.g. basetable = feature then property tabie is featureprop).
  2351. * @param $record_id
  2352. * The foreign key of the basetable to update a property for. This should be in integer.
  2353. * For example, if the basetable is 'feature' then the $record_id should be the feature_id
  2354. * @param $property
  2355. * The cvterm name of property to be updated
  2356. * @param $cv_name
  2357. * The name of the cv that the above cvterm is part of
  2358. * @param $value
  2359. * The value of the property to be inserted (can be empty)
  2360. * @param $insert_if_missing
  2361. * A boolean indicating whether a record should be inserted if one doesn't exist to update
  2362. *
  2363. * Note: The property to be updated is select via the unique combination of $record_id and
  2364. * $property and then it is updated with the supplied value
  2365. *
  2366. * @return
  2367. * Return True on Update/Insert and False otherwise
  2368. *
  2369. * @ingroup tripal_chado_api
  2370. */
  2371. function tripal_core_update_property($basetable, $record_id, $property,
  2372. $cv_name, $value, $insert_if_missing = 0) {
  2373. // first see if the property is missing (we can't update a missing property
  2374. $prop = tripal_core_get_property($basetable, $record_id, $property, $cv_name);
  2375. if (count($prop)==0) {
  2376. if ($insert_if_missing) {
  2377. return tripal_core_insert_property($basetable, $record_id, $property, $cv_name, $value);
  2378. }
  2379. else {
  2380. return FALSE;
  2381. }
  2382. }
  2383. // get the foreign key for this property table
  2384. $table_desc = tripal_core_get_chado_table_schema($basetable . 'prop');
  2385. $fkcol = key($table_desc['foreign keys'][$basetable]['columns']);
  2386. // construct the array that will match the exact record to update
  2387. $match = array(
  2388. $fkcol => $record_id,
  2389. 'type_id' => array(
  2390. 'cv_id' => array(
  2391. 'name' => $cv_name,
  2392. ),
  2393. 'name' => $property,
  2394. ),
  2395. );
  2396. // construct the array of values to be updated
  2397. $values = array(
  2398. 'value' => $value,
  2399. );
  2400. return tripal_core_chado_update($basetable . 'prop', $match, $values);
  2401. }
  2402. /**
  2403. * Update a property for a given base table record. This function should be
  2404. * used if multiple records of the same property will be present. Also, use this
  2405. * function to change the property name of an existing property.
  2406. *
  2407. * @param $basetable
  2408. * The base table for which the property should be updated. The property table
  2409. * is constructed using a combination of the base table name and the suffix
  2410. * 'prop' (e.g. basetable = feature then property tabie is featureprop).
  2411. * @param $record_id
  2412. * The primary key of the base table. This should be in integer.
  2413. * For example, if the basetable is 'feature' then the $record_id should be the featureprop_id
  2414. * @param $property
  2415. * The cvterm name of property to be updated
  2416. * @param $cv_name
  2417. * The name of the cv that the above cvterm is part of
  2418. * @param $value
  2419. * The value of the property to be inserted (can be empty)
  2420. *
  2421. * @return
  2422. * Return True on Update/Insert and False otherwise
  2423. *
  2424. * @ingroup tripal_chado_api
  2425. */
  2426. function tripal_core_update_property_by_id($basetable, $record_id, $property,
  2427. $cv_name, $value) {
  2428. // get the primary key for this property table
  2429. $table_desc = tripal_core_get_chado_table_schema($basetable . 'prop');
  2430. $pkcol = $table_desc['primary key'][0];
  2431. // construct the array that will match the exact record to update
  2432. $match = array(
  2433. $pkcol => $record_id,
  2434. );
  2435. // construct the array of values to be updated
  2436. $values = array(
  2437. 'type_id' => array(
  2438. 'cv_id' => array(
  2439. 'name' => $cv_name,
  2440. ),
  2441. 'name' => $property,
  2442. ),
  2443. 'value' => $value,
  2444. );
  2445. return tripal_core_chado_update($basetable . 'prop', $match, $values);
  2446. }
  2447. /**
  2448. * Deletes a property for a given base table record using the property name
  2449. *
  2450. * @param $basetable
  2451. * The base table for which the property should be deleted. Thus to deleted a property
  2452. * for a feature the basetable=feature and property is deleted from featureprop
  2453. * @param $record_id
  2454. * The primary key of the basetable to delete a property for. This should be in integer.
  2455. * @param $property
  2456. * The cvterm name describing the type of property to be deleted
  2457. * @param $cv_name
  2458. * The name of the cv that the above cvterm is part of
  2459. *
  2460. * Note: The property to be deleted is select via the unique combination of $record_id and $property
  2461. *
  2462. * @return
  2463. * Return True on Delete and False otherwise
  2464. *
  2465. * @ingroup tripal_chado_api
  2466. */
  2467. function tripal_core_delete_property($basetable, $record_id, $property, $cv_name) {
  2468. // get the foreign key for this property table
  2469. $table_desc = tripal_core_get_chado_table_schema($basetable . 'prop');
  2470. $fkcol = key($table_desc['foreign keys'][$basetable]['columns']);
  2471. // construct the array that will match the exact record to update
  2472. $match = array(
  2473. $fkcol => $record_id,
  2474. 'type_id' => array(
  2475. 'cv_id' => array(
  2476. 'name' => $cv_name,
  2477. ),
  2478. 'name' => $property,
  2479. ),
  2480. );
  2481. return tripal_core_chado_delete($basetable . 'prop', $match);
  2482. }
  2483. /**
  2484. * Deletes a property using the property ID
  2485. *
  2486. * @param $basetable
  2487. * The base table for which the property should be deleted. Thus to deleted a property
  2488. * for a feature the basetable=feature and property is deleted from featureprop
  2489. * @param $record_id
  2490. * The primary key of the basetable to delete a property for. This should be in integer.
  2491. *
  2492. * @return
  2493. * Return True on Delete and False otherwise
  2494. *
  2495. * @ingroup tripal_chado_api
  2496. */
  2497. function tripal_core_delete_property_by_id($basetable, $record_id) {
  2498. // get the foreign key for this property table
  2499. $table_desc = tripal_core_get_chado_table_schema($basetable . 'prop');
  2500. $pkcol = $table_desc['primary key'][0];
  2501. // construct the array that will match the exact record to update
  2502. $match = array(
  2503. $pkcol => $record_id,
  2504. );
  2505. return tripal_core_chado_delete($basetable . 'prop', $match);
  2506. }
  2507. /**
  2508. * Indicates if the SQL statement is prepapred
  2509. *
  2510. * @param $statement_name
  2511. * The name of the statement to check if it is prepared.
  2512. *
  2513. * @return
  2514. * TRUE if the statement is preapred, FALSE otherwise
  2515. */
  2516. function tripal_core_is_sql_prepared($statement_name) {
  2517. /*
  2518. global $_tripal_core_prepared_statements;
  2519. if (!is_array($_tripal_core_prepared_statements)) {
  2520. watchdog('tripal_core', "tripal_core_is_sql_prepared: argument must be an array", array(), WATCHDOG_ERROR);
  2521. return FALSE;
  2522. }
  2523. // check to see if the statement is prepared already
  2524. if (in_array($statement_name, $_tripal_core_prepared_statements)) {
  2525. return TRUE;
  2526. }
  2527. // @coder-ignore: acting on postgres tables rather then drupal schema therefore, table prefixing does not apply
  2528. $sql = "SELECT name FROM pg_prepared_statements WHERE name = :name";
  2529. $results = db_query($sql, array(':name' => $statement_name));
  2530. $result = $results->fetchObject();
  2531. if ($result) {
  2532. return TRUE;
  2533. }
  2534. return FALSE;
  2535. */
  2536. }
  2537. /**
  2538. * Prepare a chado query
  2539. *
  2540. * @param $statement_name
  2541. * The name of the prepared statement
  2542. * @param $psql
  2543. * The SQL statement to be executed via chado_query.
  2544. * Should be of the form PREPARE [statement name] AS [SQL Statement to be prepared]
  2545. * @param $args
  2546. * An array of arguements required to execute the prepared statement. The keys of
  2547. * the array should correspond to the variables in the prepared statement and the value should
  2548. * be the type of value needed (ie: text, int, etc.)
  2549. */
  2550. function tripal_core_chado_prepare($statement_name, $psql, $args) {
  2551. /*
  2552. global $_tripal_core_persistent_chado;
  2553. global $_tripal_core_prepared_statements;
  2554. if (!$_tripal_core_persistent_chado) {
  2555. watchdog('tripal_core', "chado_prepare: not able to prepare '%name' statement as no persistent connection is available", array('%name' => $statement_name, '%sql' => $psql), WATCHDOG_ERROR);
  2556. return FALSE;
  2557. }
  2558. // Check to see if this statement was already prepared
  2559. if (tripal_core_is_sql_prepared($statement_name)) {
  2560. // check that the arguments are the same
  2561. $prepared_args = $_tripal_core_prepared_statements[$statement_name]['prepared_args'];
  2562. $prepared_sql = $_tripal_core_prepared_statements[$statement_name]['prepared_sql'];
  2563. if ($prepared_args == $args) {
  2564. // This statement is already prepared
  2565. return TRUE;
  2566. }
  2567. else {
  2568. // Although a statement with this name is already prepared it is not the same!
  2569. watchdog('tripal_core', "chado_prepare: '%name' statement already prepared with different arguments! " .
  2570. "You want to prepare \n%sql\n with \n%values\n and the existing statement is \n%esql\n with \n%existing",
  2571. array('%name' => $statement_name, '%sql' => $psql, '%values' => print_r($args, TRUE), '%esql' => $prepared_sql,
  2572. '%existing' => print_r($prepared_args, TRUE)), WATCHDOG_ERROR);
  2573. return FALSE;
  2574. }
  2575. }
  2576. $status = chado_query($psql);
  2577. if (!$status) {
  2578. watchdog('tripal_core', "chado_prepare: not able to prepare '%name' statement for: %sql", array('%name' => $statement_name, '%sql' => $psql), WATCHDOG_ERROR);
  2579. return FALSE;
  2580. }
  2581. else {
  2582. $_tripal_core_prepared_statements[$statement_name] = array();
  2583. $_tripal_core_prepared_statements[$statement_name]['prepared_args'] = $args;
  2584. $_tripal_core_prepared_statements[$statement_name]['prepared_sql'] = $psql;
  2585. return TRUE;
  2586. }
  2587. */
  2588. }
  2589. /**
  2590. * Execute a prepared statement with the supplied values
  2591. *
  2592. * @param $statement_name
  2593. * The name of the prepared statement
  2594. * @param $sql
  2595. * The SQL to execute using chado query.
  2596. * Should be of the form EXECUTE [statement_name] ([arg1],[arg2]...[argn])
  2597. * @param $values
  2598. * An array of values in the execute sql statement
  2599. */
  2600. function tripal_core_chado_execute_prepared($statement_name, $sql, $values) {
  2601. /*
  2602. global $_tripal_core_prepared_statements;
  2603. if (!tripal_core_is_sql_prepared($statement_name)) {
  2604. watchdog('tripal_core', "tripal_core_chado_execute_prepared: Cannot execute an unprepared statement: '%name'", array('%name' => $statement_name), WATCHDOG_ERROR);
  2605. return FALSE;
  2606. }
  2607. // Before Executing, Ensure that all the values are supplied
  2608. $required_values = $_tripal_core_prepared_statements[$statement_name]['prepared_args'];
  2609. if (!$required_values) {
  2610. watchdog('tripal_core', "tripal_core_chado_execute_prepared: missing prepare arguments for this statement: '%name'", array('%name' => $statement_name), WATCHDOG_ERROR);
  2611. return FALSE;
  2612. }
  2613. if (sizeof($required_values) == sizeof($values)) {
  2614. $error = FALSE;
  2615. foreach ($values as $k => $v) {
  2616. if (isset($required_values[$k])) {
  2617. switch ($required_values[$k]) {
  2618. case 'text':
  2619. // anything can be converted to a string, so if the type is 'text' let's just convert it
  2620. $values[$k] = (string) $v;
  2621. break;
  2622. case 'int':
  2623. $check = is_numeric($v);
  2624. if (!$check) {
  2625. watchdog('tripal_core', "chado_execute_prepared: wrong argument type supplied for '%name' statement, field %k. Expected %required but recieved '%value'",
  2626. array('%name' => $statement_name, '%k' => $k+1, '%required' => $required_values[$k], '%value' => print_r($v, TRUE)), WATCHDOG_ERROR);
  2627. return FALSE;
  2628. }
  2629. break;
  2630. case 'bool':
  2631. if ($v != 'TRUE' and $v != 'FALSE') {
  2632. watchdog('tripal_core', "chado_execute_prepared: wrong argument type supplied for '%name' statement, field %k. Expected %required but recieved '%value'",
  2633. array('%name' => $statement_name, '%k' => $k+1, '%required' => $required_values[$k], '%value' => print_r($v, TRUE)), WATCHDOG_ERROR);
  2634. return FALSE;
  2635. }
  2636. break;
  2637. case 'numeric':
  2638. $check = is_numeric($v);
  2639. if (!$check) {
  2640. watchdog('tripal_core', "chado_execute_prepared: wrong argument type supplied for '%name' statement, field %k. Expected %required but recieved '%value'",
  2641. array('%name' => $statement_name, '%k' => $k+1, '%required' => $required_values[$k], '%value' => print_r($v, TRUE)), WATCHDOG_ERROR);
  2642. return FALSE;
  2643. }
  2644. break;
  2645. default:
  2646. watchdog('tripal_core', "chado_execute_prepared: unsupported argument type (supplied for '%name' statement %type)",
  2647. array('%name' => $statement_name, '%type' => $required_values[$k]), WATCHDOG_WARNING);
  2648. break;
  2649. }
  2650. }
  2651. else {
  2652. watchdog('tripal_core', "chado_execute_prepared: wrong number of arguments supplied for '%name' statement. Expected %required but recieved %values",
  2653. array('%name' => $statement_name, '%required' => print_r($required_values, TRUE), '%values' => print_r($values, TRUE)), WATCHDOG_ERROR);
  2654. return FALSE;
  2655. }
  2656. }
  2657. // Since all values are supplied, execute
  2658. $resource = chado_query($sql, $values);
  2659. return $resource;
  2660. }
  2661. else {
  2662. watchdog('tripal_core', "chado_execute_prepared: wrong number of arguments supplied for '%name' statement. ' .
  2663. 'Expected %required but recieved %values. Statement: %statement.",
  2664. array('%name' => $statement_name, '%required' => print_r($required_values, TRUE),
  2665. '%values' => print_r($values, TRUE), '%statement' => $_tripal_core_prepared_statements[$statement_name]['prepared_sql']), WATCHDOG_ERROR);
  2666. return FALSE;
  2667. }
  2668. */
  2669. }
  2670. /**
  2671. * Clears prepared statements to avoid conflicts
  2672. *
  2673. * If no statement_name_regex is supplied then it clears ALL prepared statements;
  2674. * Otherwise, it clears prepared statement names that match the regex provided
  2675. */
  2676. function tripal_core_chado_clear_prepared($statement_name_regex = NULL) {
  2677. /*
  2678. global $_tripal_core_prepared_statements;
  2679. if ($statement_name_regex) {
  2680. $sql = "SELECT * FROM pg_catalog.pg_prepared_statements WHERE name~:name_regex";
  2681. $resource = chado_query($sql, array(':name_regex' => $statement_name_regex));
  2682. foreach ($resource as $r) {
  2683. $k = array_search($r->name, $_tripal_core_prepared_statements);
  2684. unset($_tripal_core_prepared_statements[$k]);
  2685. chado_query('DEALLOCATE PREPARE :statement_name', array(':statement_name' => $r->name));
  2686. }
  2687. }
  2688. else {
  2689. $_tripal_core_prepared_statements = array();
  2690. chado_query('DEALLOCATE PREPARE ALL');
  2691. }
  2692. */
  2693. }
  2694. /**
  2695. * Instantiate or Return a persistent chado connection. This should not be confused with
  2696. * PHP persistent connections. Here we use the drupal db_connect function to
  2697. *
  2698. * NOTE: cannot use $active_db since a new connection is created each time
  2699. * db_set_active() is called
  2700. *
  2701. * @return
  2702. * A postgresql connection object which can be used by pg_prepare, pg_execute, etc.
  2703. */
  2704. function tripal_db_persistent_chado() {
  2705. /*
  2706. global $db_url;
  2707. global $_tripal_core_persistent_chado;
  2708. // get connection if it already exists otherwise we need to set it
  2709. if ($_tripal_core_persistent_chado) {
  2710. return $_tripal_core_persistent_chado;
  2711. }
  2712. else {
  2713. if (is_array($db_url) && isset($db_url['chado'])) {
  2714. $connection = db_connect($db_url['chado']);
  2715. if (!$connection) {
  2716. watchdog('tripal_core', "Could not create persistant connection", array(), WATCHDOG_ERROR);
  2717. return FALSE;
  2718. }
  2719. $_tripal_core_persistent_chado = $connection;
  2720. }
  2721. else {
  2722. if (is_array($db_url)) {
  2723. $connection = db_connect($db_url['default']);
  2724. }
  2725. else {
  2726. $connection = db_connect($db_url);
  2727. }
  2728. if (!$connection) {
  2729. $_tripal_core_persistent_chado = NULL;
  2730. watchdog('tripal_core', "Could not create persistant connection", array(), WATCHDOG_ERROR);
  2731. return FALSE;
  2732. }
  2733. $_tripal_core_persistent_chado = $connection;
  2734. }
  2735. return $connection;
  2736. }
  2737. return FALSE;
  2738. */
  2739. }
  2740. /**
  2741. * Release a persistent chado connection
  2742. */
  2743. function tripal_db_release_persistent_chado() {
  2744. // $_tripal_core_persistent_chado = NULL;
  2745. }
  2746. /**
  2747. * Start a transaction block. Ensures the use of a persistent chado connection
  2748. */
  2749. function tripal_db_start_transaction() {
  2750. /* $connection = tripal_db_persistent_chado();
  2751. if ($connection) {
  2752. chado_query("BEGIN");
  2753. return $connection;
  2754. }
  2755. return FALSE;
  2756. */
  2757. }
  2758. /**
  2759. * Set a savepoint to roll the current transaction back to if an error is encountered
  2760. */
  2761. function tripal_db_set_savepoint_transaction($savepoint, $release = FALSE) {
  2762. // Postgresql requires a savepoint of the same name to be unset before re-use
  2763. if ($release) {
  2764. chado_query("RELEASE SAVEPOINT :savepoint", array(':savepoint' => $savepoint));
  2765. }
  2766. chado_query("SAVEPOINT :savepoint", array(':savepoint' => $savepoint));
  2767. }
  2768. /**
  2769. * A simple function to commit a database transaction
  2770. *
  2771. * @return nothing
  2772. */
  2773. function tripal_db_commit_transaction() {
  2774. chado_query("COMMIT");
  2775. }
  2776. /**
  2777. * Rollback changes.
  2778. *
  2779. * If $savepoint is NULL then rollback to the beginning of the transaction,
  2780. * Otherwise, rollback to the point at which the named $savepoint was created
  2781. *
  2782. * @param $savepoint
  2783. * The name of the saved point in the transaction to rollback to
  2784. *
  2785. * @param $commit
  2786. * The transcation will only be committed if this value is TRUE. The
  2787. * default is TRUE.
  2788. *
  2789. * @return nothing
  2790. */
  2791. function tripal_db_rollback_transaction($savepoint = NULL, $commit = TRUE) {
  2792. if ($savepoint) {
  2793. chado_query("ROLLBACK TO SAVEPOINT :savepoint", array(':savepoint' => $savepoint));
  2794. }
  2795. else {
  2796. chado_query("ROLLBACK");
  2797. }
  2798. if ($commit) {
  2799. tripal_db_commit_transaction();
  2800. }
  2801. }
  2802. /**
  2803. * Retrieves the schema in an array for the specified custom table.
  2804. *
  2805. * @param $table
  2806. * The name of the table to create.
  2807. *
  2808. * @return
  2809. * A Drupal-style Schema API array definition of the table. Returns
  2810. * FALSE on failure.
  2811. *
  2812. * @ingroup tripal_core_api
  2813. */
  2814. function tripal_get_chado_custom_schema($table) {
  2815. $sql = "SELECT schema FROM {tripal_custom_tables} WHERE table_name = :table_name";
  2816. $results = db_query($sql, array(':table_name' => $table));
  2817. $custom = $results->fetchObject();
  2818. if (!$custom) {
  2819. return FALSE;
  2820. }
  2821. else {
  2822. return unserialize($custom->schema);
  2823. }
  2824. }
  2825. /**
  2826. * Check that any given Chado table exists. This function
  2827. * is necessary because Drupa's db_table_exists function
  2828. * hardcodes the 'public'
  2829. *
  2830. * @return
  2831. * TRUE/FALSE depending upon whether it exists
  2832. */
  2833. function tripal_chado_table_exists($table) {
  2834. global $databases;
  2835. $default_db = $databases['default']['default']['database'];
  2836. $sql = "
  2837. SELECT 1
  2838. FROM information_schema.tables
  2839. WHERE
  2840. table_name = :table_name AND
  2841. table_schema = 'chado' AND
  2842. table_catalog = '$default_db'
  2843. ";
  2844. $results = db_query($sql, array(':table_name' => $table));
  2845. $exists = $results->fetchObject();
  2846. if (!$exists) {
  2847. return FALSE;
  2848. }
  2849. return TRUE;
  2850. }
  2851. /**
  2852. * Check that the Chado schema exists within the local database
  2853. *
  2854. * @return
  2855. * TRUE/FALSE depending upon whether it exists
  2856. */
  2857. function tripal_core_chado_schema_exists() {
  2858. $exists = variable_get('chado_schema_exists', FALSE);
  2859. if (!$exists) {
  2860. // This is postgresql-specific code to check the existence of the chado schema
  2861. // @coder-ignore: acting on pg_catalog schema rather then drupal schema therefore, table prefixing does not apply
  2862. $sql = "
  2863. SELECT nspname
  2864. FROM pg_catalog.pg_namespace
  2865. WHERE nspname = 'chado'
  2866. ";
  2867. $results = db_query($sql);
  2868. $name = $results->fetchObject();
  2869. if ($name) {
  2870. variable_set('chado_schema_exists', TRUE);
  2871. return TRUE;
  2872. }
  2873. else {
  2874. return FALSE;
  2875. }
  2876. }
  2877. return TRUE;
  2878. }
  2879. /**
  2880. * Check that any given schema exists
  2881. *
  2882. * @param $schema
  2883. * The name of the schema to check the existence of
  2884. *
  2885. * @return
  2886. * TRUE/FALSE depending upon whether or not the schema exists
  2887. *
  2888. * @ingroup tripal_chado_api
  2889. */
  2890. function tripal_core_schema_exists($schema) {
  2891. // check that the chado schema now exists
  2892. $sql = "
  2893. SELECT nspname
  2894. FROM pg_namespace
  2895. WHERE
  2896. has_schema_privilege(nspname, 'USAGE') AND
  2897. nspname = :nspname
  2898. ORDER BY nspname
  2899. ";
  2900. $results = db_query($sql, array(':nspname' => $schema));
  2901. $name = $results->fetchObject();
  2902. if (strcmp($name->nspname, $schema) != 0) {
  2903. return FALSE;
  2904. }
  2905. return TRUE;
  2906. }
  2907. /**
  2908. * Retrieves the list tables in the Chado schema. By default it only retursn
  2909. * the default Chado tables, but may also return custom tables added to the
  2910. * Chado schema as well.
  2911. *
  2912. * @param $include_custom
  2913. * Optional. Set as TRUE to include any custom tables created in the
  2914. * Chado schema. Custom tables are added to Chado using the
  2915. * tripal_core_chado_create_table() function.
  2916. *
  2917. * @returns
  2918. * An associative array where the key and value pairs are the Chado table names.
  2919. *
  2920. * @ingroup tripal_core_api
  2921. */
  2922. function tripal_core_get_chado_tables($include_custom = NULL) {
  2923. // first get the chado version that is installed
  2924. $v = tripal_core_get_chado_version();
  2925. $tables = array();
  2926. if ($v == '1.2') {
  2927. $tables_v1_2 = tripal_core_chado_get_v1_2_tables();
  2928. foreach ($tables_v1_2 as $table) {
  2929. $tables[$table] = $table;
  2930. }
  2931. }
  2932. if ($v == '1.11' or $v == '1.11 or older') {
  2933. $tables_v1_11 = tripal_core_chado_get_v1_11_tables();
  2934. foreach ($tables_v1_11 as $table) {
  2935. $tables[$table] = $table;
  2936. }
  2937. }
  2938. // now add in the custom tables too if requested
  2939. if ($include_custom) {
  2940. $sql = "SELECT table_name FROM {tripal_custom_tables}";
  2941. $resource = db_query($sql);
  2942. foreach ($resource as $r) {
  2943. $tables[$r->table_name] = $r->table_name;
  2944. }
  2945. }
  2946. asort($tables);
  2947. return $tables;
  2948. }
  2949. /**
  2950. * Sets a Drupal variable with the current version of Chado. This variable
  2951. * can then be queried later using the tripal_core_get_chado_Version
  2952. *
  2953. * @returns
  2954. * The version of Chado
  2955. *
  2956. * @ingroup tripal_core_api
  2957. */
  2958. function tripal_core_set_chado_version() {
  2959. global $databases;
  2960. $is_local = 0;
  2961. // check that Chado is installed if not return 'uninstalled as the version'
  2962. $chado_exists = tripal_core_chado_schema_exists();
  2963. if (!$chado_exists) {
  2964. // if it's not in the drupal database check to see if it's specified in the $db_url
  2965. // in the settings.php
  2966. if (!array_key_exists('chado', $databases)) {
  2967. // if it's not in the drupal database or specified in the $db_url then
  2968. // return uninstalled as the version
  2969. return 'not installed';
  2970. }
  2971. $previous_db = tripal_db_set_active('chado');
  2972. $prop_exists = db_table_exists('chadoprop');
  2973. tripal_db_set_active($previous_db);
  2974. }
  2975. else {
  2976. $is_local = 1;
  2977. $prop_exists = db_table_exists('chado.chadoprop');
  2978. }
  2979. // if the table doesn't exist then we don't know what version but we know
  2980. // it must be 1.11 or older.
  2981. if (!$prop_exists) {
  2982. return "1.11 or older";
  2983. }
  2984. $sql = "
  2985. SELECT value
  2986. FROM {chadoprop} CP
  2987. INNER JOIN {cvterm} CVT on CVT.cvterm_id = CP.type_id
  2988. INNER JOIN {cv} CV on CVT.cv_id = CV.cv_id
  2989. WHERE CV.name = 'chado_properties' and CVT.name = 'version'
  2990. ";
  2991. if (!$is_local) {
  2992. $previous_db = tripal_db_set_active('chado');
  2993. $results = db_query($sql);
  2994. tripal_db_set_active($previous_db);
  2995. }
  2996. else {
  2997. $results = chado_query($sql);
  2998. }
  2999. $v = $results->fetchObject();
  3000. // if we don't have a version in the chadoprop table then it must be
  3001. // v1.11 or older
  3002. if (!$v) {
  3003. variable_set('chado_version', "1.11 or older");
  3004. return "1.11 or older";
  3005. }
  3006. variable_set('chado_version', $v->value);
  3007. return $v->value;
  3008. }
  3009. /**
  3010. * Returns the version number of the currently installed Chado instance.
  3011. * It can return the real or effective version.
  3012. *
  3013. * @param $exact
  3014. * Set this argument to 1 to retrieve the exact version that is installed.
  3015. * Otherwise, this function will set the version to the nearest 'tenth'.
  3016. * Chado versioning numbers in the hundreds represent changes to the
  3017. * software and not the schema. Changes in the tenth's represent changes
  3018. * in the schema.
  3019. *
  3020. * @param $warn_if_unsupported
  3021. * If the currently installed version of Chado is not supported by Tripal
  3022. * the generatea a Drupal warning.
  3023. *
  3024. * @returns
  3025. * The version of Chado
  3026. *
  3027. * @ingroup tripal_core_api
  3028. */
  3029. function tripal_core_get_chado_version($exact = FALSE, $warn_if_unsupported = FALSE) {
  3030. // first get the chado version that is installed
  3031. $exact_version = variable_get('chado_version', '');
  3032. if (!$exact_version) {
  3033. $exact_version = tripal_core_set_chado_version();
  3034. }
  3035. // Tripal only supports v1.11 or newer.. really this is the same as v1.1
  3036. // but at the time the v1.11 schema API was written we didn't know that so
  3037. // we'll return the version 1.11 so the schema API will work.
  3038. if (strcmp($exact_version, '1.11 or older') == 0) {
  3039. $exact_version = "1.11";
  3040. if ($warn_if_unsupported) {
  3041. drupal_set_message(t("WARNING: Tripal does not fully support Chado version less than v1.11. If you are certain this is v1.11
  3042. or if Chado was installed using an earlier version of Tripal then all is well. If not please upgrade to v1.11 or later"),
  3043. 'warning');
  3044. }
  3045. }
  3046. // if not returing an exact version, return the version to the nearest 10th.
  3047. // return 1.2 for all versions of 1.2x
  3048. $effective_version = $exact_version;
  3049. if (preg_match('/^1\.2\d+$/', $effective_version)) {
  3050. $effective_version = "1.2";
  3051. }
  3052. if ($warn_if_unsupported and ($effective_version != 1.11 and $effective_version != 1.2 and $effective_version != 'not installed')) {
  3053. drupal_set_message(t("WARNING: The currently installed version of Chado, v$exact_version, is not fully compatible with Tripal."), 'warning');
  3054. }
  3055. // if the callee has requested the exact version then return it
  3056. if ($exact) {
  3057. return $exact_version;
  3058. }
  3059. return $effective_version;
  3060. }
  3061. /**
  3062. * Retrieves the chado tables Schema API array.
  3063. *
  3064. * @param $table
  3065. * The name of the table to retrieve. The function will use the appopriate
  3066. * Tripal chado schema API hooks (e.g. v1.11 or v1.2).
  3067. *
  3068. * @returns
  3069. * A Drupal Schema API array defining the table.
  3070. *
  3071. * @ingroup tripal_core_api
  3072. */
  3073. function tripal_core_get_chado_table_schema($table) {
  3074. // first get the chado version that is installed
  3075. $v = tripal_core_get_chado_version();
  3076. // get the table array from the proper chado schema
  3077. $v = preg_replace("/\./", "_", $v); // reformat version for hook name
  3078. $table_arr = module_invoke_all("chado_schema_v" . $v . "_" . $table);
  3079. // if the table_arr is empty then maybe this is a custom table
  3080. if (!is_array($table_arr) or count($table_arr) == 0) {
  3081. $table_arr = tripal_get_chado_custom_schema($table);
  3082. }
  3083. return $table_arr;
  3084. }
  3085. /**
  3086. * This function will delete Drupal nodes for any sync'ed table (e.g.
  3087. * feature, organism, analysis, stock, library) if the chado record has been
  3088. * deleted or the entry in the chado_[table] table has been removed.
  3089. *
  3090. * @param $table
  3091. * The name of the table that corresonds to the node type we want to clean up.
  3092. * @param $job_id
  3093. * This should be the job id from the Tripal jobs system. This function
  3094. * will update the job status using the provided job ID.
  3095. *
  3096. * @ingroup tripal_core_api
  3097. */
  3098. function tripal_core_clean_orphaned_nodes($table, $job_id) {
  3099. $count = 0;
  3100. // build the SQL statments needed to check if nodes point to valid analyses
  3101. $dsql = "SELECT * FROM {node} WHERE type = 'chado_" . $table . "' order by nid";
  3102. $nsql = "SELECT * FROM {node} WHERE nid = :nid";
  3103. $csql = "SELECT * FROM {chado_" . $table . "} WHERE nid = :nid ";
  3104. $clsql= "SELECT * FROM {chado_" . $table . "}";
  3105. $lsql = "SELECT * FROM {" . $table . "} where " . $table . "_id = :" . $table . "_id ";
  3106. // load into nodes array
  3107. print "Getting nodes\n";
  3108. $nodes = array();
  3109. $res = db_query($dsql);
  3110. foreach ($res as $node) {
  3111. $nodes[$count] = $node;
  3112. $count++;
  3113. }
  3114. // load the chado_$table into an array
  3115. print "Getting chado_$table\n";
  3116. $cnodes = array();
  3117. $res = db_query($clsql);
  3118. foreach ($res as $node) {
  3119. $cnodes[$count] = $node;
  3120. $count++;
  3121. }
  3122. $interval = intval($count * 0.01);
  3123. if ($interval < 1) {
  3124. $interval = 1;
  3125. }
  3126. // iterate through all of the chado_$table entries and remove those
  3127. // that don't have a node or don't have a $table record in chado.libary
  3128. print "Verifying all chado_$table Entries\n";
  3129. $deleted = 0;
  3130. foreach ($cnodes as $nid) {
  3131. // update the job status every 1% analyses
  3132. if ($job_id and $i % $interval == 0) {
  3133. tripal_job_set_progress($job_id, intval(($i / $count) * 100));
  3134. }
  3135. // see if the node exits, if not remove the entry from the chado_$table table
  3136. $results = db_query($nsql, array(':nid' => $nid->nid));
  3137. $node = $results->fetchObject();
  3138. if (!$node) {
  3139. $deleted++;
  3140. db_query("DELETE FROM {chado_" . $table . "} WHERE nid = :nid", array(':nid' => $nid->nid));
  3141. $message = "chado_$table missing node.... DELETING: $nid->nid";
  3142. watchdog('tripal_core', $message, array(), WATCHDOG_WARNING);
  3143. }
  3144. // see if the record in chado exist, if not remove the entry from the chado_$table
  3145. $table_id = $table . "_id";
  3146. $results = chado_query($lsql, array(":" . $table . "_id " => $nid->$table_id));
  3147. $record = $results->fetchObject();
  3148. if (!$record) {
  3149. $deleted++;
  3150. $sql = "DELETE FROM {chado_" . $table . "} WHERE " . $table . "_id = :" . $table . "_id";
  3151. db_query($sql, array(":" . $table . "_id" => $nid->$table_id));
  3152. $message = "chado_$table missing $table.... DELETING entry.";
  3153. watchdog('tripal_core', $message, array(), WATCHDOG_WARNING);
  3154. }
  3155. $i++;
  3156. }
  3157. print "\t$deleted chado_$table entries missing either a node or chado entry.\n";
  3158. // iterate through all of the nodes and delete those that don't
  3159. // have a corresponding entry in chado_$table
  3160. $deleted = 0;
  3161. foreach ($nodes as $node) {
  3162. // update the job status every 1% libraries
  3163. if ($job_id and $i % $interval == 0) {
  3164. tripal_job_set_progress($job_id, intval(($i / $count) * 100));
  3165. }
  3166. // check to see if the node has a corresponding entry
  3167. // in the chado_$table table. If not then delete the node.
  3168. $results = db_query($csql, array(":nid" => $node->nid));
  3169. $link = $results->fetchObject();
  3170. if (!$link) {
  3171. if (node_access('delete', $node)) {
  3172. $deleted++;
  3173. $message = "Node missing in chado_$table table.... DELETING node $node->nid";
  3174. watchdog("tripal_core", $message, array(), WATCHDOG_WARNING);
  3175. node_delete($node->nid);
  3176. }
  3177. else {
  3178. $message = "Node missing in chado_$table table.... but cannot delete due to improper permissions (node $node->nid)";
  3179. watchdog("tripal_core", $message, array(), WATCHDOG_WARNING);
  3180. }
  3181. }
  3182. $i++;
  3183. }
  3184. print "\t$deleted nodes did not have corresponding chado_$table entries.\n";
  3185. return '';
  3186. }
  3187. /**
  3188. * Check whether chado is installed (either in the same or a different database)
  3189. *
  3190. * @return
  3191. * TRUE/FALSE depending upon whether chado is installed.
  3192. *
  3193. * @ingroup tripal_chado_api
  3194. */
  3195. function tripal_core_is_chado_installed() {
  3196. global $databases;
  3197. // first check if chado is in the $databases variable of the settings.php file
  3198. if (array_key_exists('chado', $databases)) {
  3199. return TRUE;
  3200. }
  3201. // check to make sure the chado schema exists
  3202. return tripal_core_chado_schema_exists();
  3203. }
  3204. /**
  3205. * Check whether chado is installed local to the Drupal database
  3206. * in its own Chado schema.
  3207. *
  3208. * @return
  3209. * TRUE/FALSE depending upon whether chado is local.
  3210. *
  3211. * @ingroup tripal_chado_api
  3212. */
  3213. function tripal_core_is_chado_local() {
  3214. global $db_url, $db_type;
  3215. $is_installed = tripal_core_is_chado_installed();
  3216. if ($is_installed) {
  3217. if (is_array($db_url)) {
  3218. if (isset($db_url['chado'])) {
  3219. return FALSE;
  3220. }
  3221. }
  3222. return TRUE;
  3223. }
  3224. return FALSE;
  3225. }
  3226. /**
  3227. * Determine whether a given chado table is directly linked to a node
  3228. *
  3229. * @param $chado_table
  3230. * The name of a chado table to check (ie: feature)
  3231. * @return
  3232. * TRUE if it is linked to a node and FALSE otherwise
  3233. */
  3234. function tripal_core_is_tripal_node_type($chado_table) {
  3235. $linking_table = 'chado_' . $chado_table;
  3236. if (db_table_exists($linking_table)) {
  3237. return TRUE;
  3238. }
  3239. else {
  3240. return FALSE;
  3241. }
  3242. }
  3243. /**
  3244. * Set the Tripal Database
  3245. *
  3246. * The tripal_db_set_active function is used to prevent namespace collisions
  3247. * when chado and drupal are installed in the same database but in different
  3248. * schemas. It is also used for backwards compatibility with older versions
  3249. * of tripal or in cases where chado is located outside of the Drupal database.
  3250. * or when using Drupal functions such as db_table_exists()
  3251. *
  3252. * @ingroup tripal_chado_api
  3253. */
  3254. function tripal_db_set_active($dbname = 'default') {
  3255. global $databases, $active_db;
  3256. $chado_exists = variable_get('chado_schema_exists', FALSE);
  3257. if ($chado_exists) {
  3258. if($dbname == 'chado') {
  3259. db_query('set search_path to chado');
  3260. return 'default';
  3261. }
  3262. else {
  3263. db_query('set search_path to public');
  3264. return 'chado';
  3265. }
  3266. }
  3267. // if the 'chado' database is in the $db_url variable then chado is
  3268. // not in the same Drupal database, so we don't need to set any
  3269. // search_path and can just change the database
  3270. elseif (array_key_exists($dbname, $databases)) {
  3271. return db_set_active($dbname);
  3272. }
  3273. }