tripal_core_mviews.api.inc 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382
  1. <?php
  2. /**
  3. * @file
  4. * Contains functions for the Materialized Views API
  5. * @defgroup tripal_mviews_api Materalized Views API
  6. * @ingroup tripal_core_api
  7. * @{
  8. * Provides an application programming interface (API) to manage materialized views in Chado.
  9. * The Perl-based chado comes with an interface for managing materialzed views. This
  10. * API provides an alternative Drupal-based method.
  11. * @}
  12. */
  13. /**
  14. * Add a materialized view to the chado database to help speed data access. This
  15. * function supports the older style where postgres column specifications
  16. * are provided using the $mv_table, $mv_specs and $indexed variables. It also
  17. * supports the newer preferred method where the materialized view is described
  18. * using the Drupal Schema API array.
  19. *
  20. * @param $name
  21. * The name of the materialized view.
  22. * @param $modulename
  23. * The name of the module submitting the materialized view (e.g. 'tripal_library')
  24. * @param $mv_table
  25. * The name of the table to add to chado. This is the table that can be queried.
  26. * @param $mv_specs
  27. * The table definition
  28. * @param $indexed
  29. * The columns that are to be indexed
  30. * @param $query
  31. * The SQL query that loads the materialized view with data
  32. * @param $special_index
  33. * currently not used
  34. * @param $comment
  35. * A string containing a description of the materialized view
  36. * @param $mv_schema
  37. * If using the newer Schema API array to define the materialized view then
  38. * this variable should contain the array or a string representation of the
  39. * array.
  40. *
  41. * @ingroup tripal_mviews_api
  42. */
  43. function tripal_add_mview($name, $modulename, $mv_table, $mv_specs, $indexed,
  44. $query, $special_index, $comment = NULL, $mv_schema = NULL) {
  45. // get the table name from the schema array
  46. $schema_arr = array();
  47. if ($mv_schema) {
  48. // if the schema is provided as a string then convert it to an array
  49. if (!is_array($mv_schema)) {
  50. eval("\$schema_arr = $mv_schema;");
  51. }
  52. // if the schema is provided as an array then create a string
  53. // copy of it for storage in the mview
  54. else {
  55. $schema_arr = $mv_schema;
  56. $mv_schema = var_export($schema_arr, 1);
  57. }
  58. $mv_table = $schema_arr['table'];
  59. }
  60. // Create a new record
  61. $record = new stdClass();
  62. $record->name = $name;
  63. $record->modulename = $modulename;
  64. $record->mv_table = $mv_table;
  65. $record->mv_specs = $mv_specs;
  66. $record->indexed = $indexed;
  67. $record->query = $query;
  68. $record->special_index = $special_index;
  69. $record->comment = $comment;
  70. $record->mv_schema = $mv_schema;
  71. // add the record to the tripal_mviews table and if successful
  72. // create the new materialized view in the chado schema
  73. if (drupal_write_record('tripal_mviews', $record)) {
  74. // drop the table from chado if it exists
  75. if (chado_table_exists($mv_table)) {
  76. $sql = "DROP TABLE {$mv_table}";
  77. chado_query($sql);
  78. }
  79. // now construct the indexes
  80. $index = '';
  81. if ($indexed) {
  82. // add to the array of values
  83. $vals = preg_split("/[\n,]+/", $indexed);
  84. $index = '';
  85. foreach ($vals as $field) {
  86. $field = trim($field);
  87. $index .= "CREATE INDEX idx_${mv_table}_${field} ON $mv_table ($field);";
  88. }
  89. }
  90. // create the table differently depending on if it the traditional method
  91. // or the Drupal Schema API method
  92. if ($mv_schema) {
  93. if (!tripal_core_create_custom_table ($mv_table, $schema_arr, 0)) {
  94. drupal_set_message(t("Could not create the materialized view. Check Drupal error report logs."), 'error');
  95. }
  96. else {
  97. drupal_set_message(t("View '%name' created", array('%name' => $name)));
  98. }
  99. }
  100. else {
  101. // add the table to the database
  102. $sql = "CREATE TABLE {$mv_table} ($mv_specs); $index";
  103. $previous_db = tripal_db_set_active('chado'); // use chado database
  104. $results = db_query($sql);
  105. tripal_db_set_active($previous_db); // now use drupal database
  106. if ($results) {
  107. drupal_set_message(t("View '%name' created", array('%name' => $name)));
  108. }
  109. else {
  110. drupal_set_message(t("Failed to create the materialized view table: '%mv_table'", array('%mv_table' => $mv_table)), 'error');
  111. }
  112. }
  113. }
  114. }
  115. /**
  116. * Edits a materialized view to the chado database to help speed data access.This
  117. * function supports the older style where postgres column specifications
  118. * are provided using the $mv_table, $mv_specs and $indexed variables. It also
  119. * supports the newer preferred method where the materialized view is described
  120. * using the Drupal Schema API array.
  121. *
  122. * @param $mview_id
  123. * The mview_id of the materialized view to edit
  124. * @param $name
  125. * The name of the materialized view.
  126. * @param $modulename
  127. * The name of the module submitting the materialized view (e.g. 'tripal_library')
  128. * @param $mv_table
  129. * The name of the table to add to chado. This is the table that can be queried.
  130. * @param $mv_specs
  131. * The table definition
  132. * @param $indexed
  133. * The columns that are to be indexed
  134. * @param $query
  135. * The SQL query that loads the materialized view with data
  136. * @param $special_index
  137. * currently not used
  138. * @param $comment
  139. * A string containing a description of the materialized view
  140. * @param $mv_schema
  141. * If using the newer Schema API array to define the materialized view then
  142. * this variable should contain the array.
  143. *
  144. * @ingroup tripal_mviews_api
  145. */
  146. function tripal_edit_mview($mview_id, $name, $modulename, $mv_table, $mv_specs,
  147. $indexed, $query, $special_index, $comment = NULL, $mv_schema = NULL) {
  148. // get the table name from the schema array
  149. $schema_arr = array();
  150. if ($mv_schema) {
  151. // get the schema from the mv_specs and use it to add the custom table
  152. eval("\$schema_arr = $mv_schema;");
  153. $mv_table = $schema_arr['table'];
  154. }
  155. $record = new stdClass();
  156. $record->mview_id = $mview_id;
  157. $record->name = $name;
  158. $record->modulename = $modulename;
  159. $record->query = $query;
  160. $record->last_update = 0;
  161. $record->status = '';
  162. $record->comment = $comment;
  163. // get the view before we update and check to see if the table structure has
  164. // changed. IF so, then we want to drop and recreate the table. If not, then
  165. // just save the updated SQL.
  166. $create_table = 1;
  167. $sql = "SELECT * FROM {tripal_mviews} WHERE mview_id = :mview_id";
  168. $results = db_query($sql, array(':mview_id' => $mview_id));
  169. $mview = $results->fetchObject();
  170. if ($mview->mv_schema == $mv_schema and $mview->mv_table == $mv_table and
  171. $mview->mv_specs == $mv_specs and $mview->indexed == $indexed and
  172. $mview->special_index == $special_index) {
  173. // nothing has changed so simpy update the SQL and other fields
  174. $create_table = 0;
  175. }
  176. else {
  177. // add in the table structure fields
  178. $record->mv_schema = $mv_schema;
  179. $record->mv_table = $mv_table;
  180. $record->mv_specs = $mv_specs;
  181. $record->indexed = $indexed;
  182. $record->query = $query;
  183. $record->special_index = $special_index;
  184. }
  185. // if we are going to create the table then we must first drop it if it exists
  186. if ($create_table) {
  187. $previous_db = tripal_db_set_active('chado'); // use chado database
  188. if (db_table_exists($mview->mv_table)) {
  189. $sql = "DROP TABLE :table_name";
  190. db_query($sql, array(':table_name' => $mview->mv_table));
  191. drupal_set_message(t("View '%name' dropped", array('%name' => $name)));
  192. }
  193. tripal_db_set_active($previous_db); // now use drupal database
  194. }
  195. // update the record to the tripal_mviews table and if successful
  196. // create the new materialized view in the chado schema
  197. if (drupal_write_record('tripal_mviews', $record, 'mview_id')) {
  198. // construct the indexes SQL if needed
  199. $index = '';
  200. if ($indexed) {
  201. // add to the array of values
  202. $vals = preg_split("/[\n,]+/", $indexed);
  203. $index = '';
  204. foreach ($vals as $field) {
  205. $field = trim($field);
  206. $index .= "CREATE INDEX idx_${mv_table}_${field} ON $mv_table ($field);";
  207. }
  208. }
  209. // re-create the table differently depending on if it the traditional method
  210. // or the Drupal Schema API method
  211. if ($create_table and $mv_schema) {
  212. if (!tripal_core_create_custom_table($mv_table, $schema_arr, 0)) {
  213. drupal_set_message(t("Could not create the materialized view. Check Drupal error report logs."));
  214. }
  215. else {
  216. drupal_set_message(t("View '%name' created", array('%name' => $name)));
  217. }
  218. }
  219. if ($create_table and !$mv_schema) {
  220. $sql = "CREATE TABLE {$mv_table} ($mv_specs); $index";
  221. $results = chado_query($sql);
  222. if ($results) {
  223. drupal_set_message(t("View '%name' created. All records cleared. Please re-populate the view.",
  224. array('%name' => $name)));
  225. }
  226. else {
  227. drupal_set_message(t("Failed to create the materialized view table: '%mv_table'",
  228. array('%mv_table' => $mv_table)), 'error');
  229. }
  230. }
  231. if (!$create_table) {
  232. $message = "View '%name' updated. All records remain. ";
  233. if ($query != $mview->query) {
  234. $message .= "Please repopulate the view to use updated query.";
  235. }
  236. drupal_set_message(t($message, array('%name' => $name)));
  237. }
  238. }
  239. else {
  240. drupal_set_message(t("Failed to update the materialized view: '%mv_table'",
  241. array('%mv_table' => $mv_table)), 'error');
  242. }
  243. }
  244. /**
  245. * Retrieve the materialized view_id given the name
  246. *
  247. * @param $view_name
  248. * The name of the materialized view
  249. *
  250. * @return
  251. * The unique identifier for the given view
  252. *
  253. * @ingroup tripal_mviews_api
  254. */
  255. function tripal_mviews_get_mview_id($view_name) {
  256. if (db_table_exists('tripal_mviews')) {
  257. $sql = "SELECT * FROM {tripal_mviews} WHERE name = :name";
  258. $results = db_query($sql, array(':name' => $view_name));
  259. $mview = $results->fetchObject();
  260. if ($mview) {
  261. return $mview->mview_id;
  262. }
  263. }
  264. return FALSE;
  265. }
  266. /**
  267. * Does the specified action for the specified Materialized View
  268. *
  269. * @param $op
  270. * The action to be taken. One of update or delete
  271. * @param $mview_id
  272. * The unique ID of the materialized view for the action to be performed on
  273. * @param $redirect
  274. * TRUE/FALSE depending on whether you want to redirect the user to admin/tripal/mviews
  275. *
  276. * @ingroup tripal_core
  277. */
  278. function tripal_mviews_action($op, $mview_id, $redirect = FALSE) {
  279. global $user;
  280. if (!$mview_id) {
  281. return '';
  282. }
  283. // get this mview details
  284. $sql = "SELECT * FROM {tripal_mviews} WHERE mview_id = :mview_id";
  285. $results = db_query($sql, array(':mview_id' => $mview_id));
  286. $mview = $results->fetchObject();
  287. // add a job or perform the action based on the given operation
  288. if ($op == 'update') {
  289. $args = array("$mview_id");
  290. tripal_add_job("Populate materialized view '$mview->name'", 'tripal_core',
  291. 'tripal_update_mview', $args, $user->uid);
  292. }
  293. if ($op == 'delete') {
  294. // remove the mview from the tripal_mviews table
  295. $sql = "DELETE FROM {tripal_mviews} " .
  296. "WHERE mview_id = $mview_id";
  297. db_query($sql);
  298. // drop the table from chado if it exists
  299. $previous_db = tripal_db_set_active('chado'); // use chado database
  300. if (db_table_exists($mview->mv_table)) {
  301. $sql = "DROP TABLE " . $mview->mv_table;
  302. db_query($sql);
  303. }
  304. tripal_db_set_active($previous_db); // now use drupal database
  305. }
  306. // Redirect the user
  307. if ($redirect) {
  308. drupal_goto("admin/tripal/mviews");
  309. }
  310. }
  311. /**
  312. * Update a Materialized View
  313. *
  314. * @param $mview_id
  315. * The unique identifier for the materialized view to be updated
  316. *
  317. * @return
  318. * True if successful, FALSE otherwise
  319. *
  320. * @ingroup tripal_mviews_api
  321. */
  322. function tripal_update_mview($mview_id) {
  323. $sql = "SELECT * FROM {tripal_mviews} WHERE mview_id = :mview_id ";
  324. $results = db_query($sql, array(':mview_id' => $mview_id));
  325. $mview = $results->fetchObject();
  326. if ($mview) {
  327. // execute the query inside a transaction so that it doesn't destroy existing data
  328. // that may leave parts of the site unfunctional
  329. tripal_db_start_transaction();
  330. $previous_db = tripal_db_set_active('chado'); // use chado database
  331. $results = db_query("DELETE FROM {" . $mview->mv_table . "}");
  332. $results = db_query("INSERT INTO {" . $mview->mv_table . "} ($mview->query)");
  333. tripal_db_set_active($previous_db); // now use drupal database
  334. if ($results) {
  335. // commit the transaction
  336. tripal_db_commit_transaction();
  337. $sql = "SELECT count(*) as cnt FROM {" . $mview->mv_table . "}";
  338. $results = chado_query($sql);
  339. $count = $results->fetchObject();
  340. $record = new stdClass();
  341. $record->mview_id = $mview_id;
  342. $record->last_update = REQUEST_TIME;
  343. $record->status = "Populated with " . number_format($count->cnt) . " rows";
  344. drupal_write_record('tripal_mviews', $record, 'mview_id');
  345. return TRUE;
  346. }
  347. else {
  348. // rollback the transaction
  349. tripal_db_rollback_transaction();
  350. // print and save the error message
  351. $record = new stdClass();
  352. $record->mview_id = $mview_id;
  353. $record->status = "ERROR populating. See Drupal's recent log entries for details.";
  354. print $record->status . "\n";
  355. drupal_write_record('tripal_mviews', $record, 'mview_id');
  356. return FALSE;
  357. }
  358. }
  359. }