mviews.php 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803
  1. <?php
  2. /**
  3. * @file
  4. * Contains functions for the Materialized Views API
  5. * @defgroup tripal_mviews_api Core Module Materalized Views API
  6. * @{
  7. * Provides an application programming interface (API) to manage materialized views in Chado.
  8. * The Perl-based chado comes with an interface for managing materialzed views. This
  9. * API provides an alternative Drupal-based method.
  10. * @}
  11. * @ingroup tripal_api
  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. $previous_db = tripal_db_set_active('chado'); // use chado database
  76. if (db_table_exists($mv_table)) {
  77. $sql = "DROP TABLE $mv_table";
  78. db_query($sql);
  79. }
  80. tripal_db_set_active($previous_db); // now use drupal database
  81. // now construct the indexes
  82. $index = '';
  83. if ($indexed) {
  84. // add to the array of values
  85. $vals = preg_split("/[\n,]+/", $indexed);
  86. $index = '';
  87. foreach ($vals as $field) {
  88. $field = trim($field);
  89. $index .= "CREATE INDEX idx_${mv_table}_${field} ON $mv_table ($field);";
  90. }
  91. }
  92. // create the table differently depending on if it the traditional method
  93. // or the Drupal Schema API method
  94. if ($mv_schema) {
  95. if (!tripal_core_create_custom_table ($ret, $mv_table, $schema_arr, 0)) {
  96. drupal_set_message(t("Could not create the materialized view. Check Drupal error report logs."), 'error');
  97. }
  98. else {
  99. drupal_set_message(t("View '%name' created", array('%name' => $name)));
  100. }
  101. }
  102. else {
  103. // add the table to the database
  104. $sql = "CREATE TABLE {$mv_table} ($mv_specs); $index";
  105. $previous_db = tripal_db_set_active('chado'); // use chado database
  106. $results = db_query($sql);
  107. tripal_db_set_active($previous_db); // now use drupal database
  108. if ($results) {
  109. drupal_set_message(t("View '%name' created", array('%name' => $name)));
  110. }
  111. else {
  112. drupal_set_message(t("Failed to create the materialized view table: '%mv_table'", array('%mv_table' => $mv_table)), 'error');
  113. }
  114. }
  115. }
  116. }
  117. /**
  118. * Edits a materialized view to the chado database to help speed data access.This
  119. * function supports the older style where postgres column specifications
  120. * are provided using the $mv_table, $mv_specs and $indexed variables. It also
  121. * supports the newer preferred method where the materialized view is described
  122. * using the Drupal Schema API array.
  123. *
  124. * @param $mview_id
  125. * The mview_id of the materialized view to edit
  126. * @param $name
  127. * The name of the materialized view.
  128. * @param $modulename
  129. * The name of the module submitting the materialized view (e.g. 'tripal_library')
  130. * @param $mv_table
  131. * The name of the table to add to chado. This is the table that can be queried.
  132. * @param $mv_specs
  133. * The table definition
  134. * @param $indexed
  135. * The columns that are to be indexed
  136. * @param $query
  137. * The SQL query that loads the materialized view with data
  138. * @param $special_index
  139. * currently not used
  140. * @param $comment
  141. * A string containing a description of the materialized view
  142. * @param $mv_schema
  143. * If using the newer Schema API array to define the materialized view then
  144. * this variable should contain the array.
  145. *
  146. * @ingroup tripal_mviews_api
  147. */
  148. function tripal_edit_mview($mview_id, $name, $modulename, $mv_table, $mv_specs,
  149. $indexed, $query, $special_index, $comment = NULL, $mv_schema = NULL) {
  150. // get the table name from the schema array
  151. $schema_arr = array();
  152. if ($mv_schema) {
  153. // get the schema from the mv_specs and use it to add the custom table
  154. eval("\$schema_arr = $mv_schema;");
  155. $mv_table = $schema_arr['table'];
  156. }
  157. $record = new stdClass();
  158. $record->mview_id = $mview_id;
  159. $record->name = $name;
  160. $record->modulename = $modulename;
  161. $record->query = $query;
  162. $record->last_update = 0;
  163. $record->status = '';
  164. $record->comment = $comment;
  165. // get the view before we update and check to see if the table structure has
  166. // changed. IF so, then we want to drop and recreate the table. If not, then
  167. // just save the updated SQL.
  168. $create_table = 1;
  169. $sql = "SELECT * FROM {tripal_mviews} WHERE mview_id = %d";
  170. $mview = db_fetch_object(db_query($sql, $mview_id));
  171. if($mview->mv_schema == $mv_schema and $mview->mv_table == $mv_table and
  172. $mview->mv_specs == $mv_specs and $mview->indexed == $indexed and
  173. $mview->special_index == $special_index) {
  174. // nothing has changed so simpy update the SQL and other fields
  175. $create_table = 0;
  176. }
  177. else {
  178. // add in the table structure fields
  179. $record->mv_schema = $mv_schema;
  180. $record->mv_table = $mv_table;
  181. $record->mv_specs = $mv_specs;
  182. $record->indexed = $indexed;
  183. $record->query = $query;
  184. $record->special_index = $special_index;
  185. }
  186. // if we are going to create the table then we must first drop it if it exists
  187. if ($create_table) {
  188. $previous_db = tripal_db_set_active('chado'); // use chado database
  189. if (db_table_exists($mview->mv_table)) {
  190. $sql = "DROP TABLE %s";
  191. db_query($sql, $mview->mv_table);
  192. drupal_set_message(t("View '%name' dropped", array('%name' => $name)));
  193. }
  194. tripal_db_set_active($previous_db); // now use drupal database
  195. }
  196. // update the record to the tripal_mviews table and if successful
  197. // create the new materialized view in the chado schema
  198. if (drupal_write_record('tripal_mviews', $record, 'mview_id')) {
  199. // construct the indexes SQL if needed
  200. $index = '';
  201. if ($indexed) {
  202. // add to the array of values
  203. $vals = preg_split("/[\n,]+/", $indexed);
  204. $index = '';
  205. foreach ($vals as $field) {
  206. $field = trim($field);
  207. $index .= "CREATE INDEX idx_${mv_table}_${field} ON $mv_table ($field);";
  208. }
  209. }
  210. // re-create the table differently depending on if it the traditional method
  211. // or the Drupal Schema API method
  212. if ($create_table and $mv_schema) {
  213. if (!tripal_core_create_custom_table($ret, $mv_table, $schema_arr, 0)) {
  214. drupal_set_message(t("Could not create the materialized view. Check Drupal error report logs."));
  215. }
  216. else {
  217. drupal_set_message(t("View '%name' created", array('%name' => $name)));
  218. }
  219. }
  220. if ($create_table and !$mv_schema) {
  221. $sql = "CREATE TABLE {$mv_table} ($mv_specs); $index";
  222. $results = chado_query($sql);
  223. if ($results) {
  224. drupal_set_message(t("View '%name' created. All records cleared. Please re-populate the view.",
  225. array('%name' => $name)));
  226. }
  227. else {
  228. drupal_set_message(t("Failed to create the materialized view table: '%mv_table'",
  229. array('%mv_table' => $mv_table)), 'error');
  230. }
  231. }
  232. if (!$create_table) {
  233. $message = "View '%name' updated. All records remain. ";
  234. if ($query != $mview->query) {
  235. $message .= "Please repopulate the view to use updated query.";
  236. }
  237. drupal_set_message(t($message, array('%name' => $name)));
  238. }
  239. }
  240. else {
  241. drupal_set_message(t("Failed to update the materialized view: '%mv_table'",
  242. array('%mv_table' => $mv_table)), 'error');
  243. }
  244. }
  245. /**
  246. * Retrieve the materialized view_id given the name
  247. *
  248. * @param $view_name
  249. * The name of the materialized view
  250. *
  251. * @return
  252. * The unique identifier for the given view
  253. *
  254. * @ingroup tripal_mviews_api
  255. */
  256. function tripal_mviews_get_mview_id($view_name) {
  257. $sql = "SELECT * FROM {tripal_mviews} ".
  258. "WHERE name = '%s'";
  259. if (db_table_exists('tripal_mviews')) {
  260. $mview = db_fetch_object(db_query($sql, $view_name));
  261. if ($mview) {
  262. return $mview->mview_id;
  263. }
  264. }
  265. return FALSE;
  266. }
  267. /**
  268. * Does the specified action for the specified Materialized View
  269. *
  270. * @param $op
  271. * The action to be taken. One of update or delete
  272. * @param $mview_id
  273. * The unique ID of the materialized view for the action to be performed on
  274. * @param $redirect
  275. * TRUE/FALSE depending on whether you want to redirect the user to admin/tripal/mviews
  276. *
  277. * @ingroup tripal_core
  278. */
  279. function tripal_mviews_action($op, $mview_id, $redirect = FALSE) {
  280. global $user;
  281. $args = array("$mview_id");
  282. if (!$mview_id) {
  283. return '';
  284. }
  285. // get this mview details
  286. $sql = "SELECT * FROM {tripal_mviews} WHERE mview_id = %d";
  287. $mview = db_fetch_object(db_query($sql, $mview_id));
  288. // add a job or perform the action based on the given operation
  289. if ($op == 'update') {
  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 = %d ";
  324. $mview = db_fetch_object(db_query($sql, $mview_id));
  325. if ($mview) {
  326. // execute the query inside a transaction so that it doesn't destroy existing data
  327. // that may leave parts of the site unfunctional
  328. tripal_db_start_transaction();
  329. $previous_db = tripal_db_set_active('chado'); // use chado database
  330. $results = db_query("DELETE FROM {%s}", $mview->mv_table);
  331. $results = db_query("INSERT INTO {%s} ($mview->query)", $mview->mv_table);
  332. tripal_db_set_active($previous_db); // now use drupal database
  333. if ($results) {
  334. // commit the transaction
  335. tripal_db_commit_transaction();
  336. $sql = "SELECT count(*) as cnt FROM {%s}";
  337. $previous_db = tripal_db_set_active('chado'); // use chado database
  338. $count = db_fetch_object(db_query($sql, $mview->mv_table));
  339. tripal_db_set_active($previous_db); // now use drupal database
  340. $record = new stdClass();
  341. $record->mview_id = $mview_id;
  342. $record->last_update = 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. }
  360. /**
  361. * A template function which returns markup to display details for the current materialized view
  362. *
  363. * @param $mview_id
  364. * The unique ID of the materialized view to render
  365. *
  366. * @ingroup tripal_mviews_api
  367. */
  368. function tripal_mview_report($mview_id) {
  369. // get this mview details
  370. $sql = "SELECT * FROM {tripal_mviews} WHERE mview_id = %d";
  371. $mview = db_fetch_object(db_query($sql, $mview_id));
  372. $rows = array();
  373. // create a table with each row containig stats for
  374. // an individual job in the results set.
  375. $return_url = url("admin/tripal/mviews/");
  376. $output .= "<p><a href=\"$return_url\">Return to table of materialized views.</a></p>";
  377. $output .= "<p>Details for <b>$mview->name</b>:</p>";
  378. // build the URLs using the url function so we can handle installations where
  379. // clean URLs are or are not used
  380. $update_url = url("admin/tripal/mviews/action/update/$mview->mview_id");
  381. $delete_url = url("admin/tripal/mviews/action/delete/$mview->mview_id");
  382. $edit_url = url("admin/tripal/mviews/edit/$mview->mview_id");
  383. $rows[] = array('Actions', "<a href='$update_url'>Populate</a>, <a href='$edit_url'>Edit</a>, <a href='$delete_url'>Delete</a>");
  384. if ($mview->last_update > 0) {
  385. $update = format_date($mview->last_update);
  386. }
  387. else {
  388. $update = 'Not yet populated';
  389. }
  390. $rows[] = array('Last Update', $update);
  391. if ($mview->name) {
  392. $rows[] = array('View Name', $mview->name);
  393. }
  394. if ($mview->modulename) {
  395. $rows[] = array('Module Name', $mview->modulename);
  396. }
  397. if ($mview->mv_table) {
  398. $rows[] = array('Table Name', $mview->mv_table);
  399. }
  400. if ($mview->mv_specs) {
  401. $rows[] = array('Table Field Definitions', $mview->mv_specs);
  402. }
  403. if ($mview->query) {
  404. $rows[] = array('Query', "<pre>" . $mview->query . "</pre>");
  405. }
  406. if ($mview->indexed) {
  407. $rows[] = array('Indexed Fields', $mview->indexed);
  408. }
  409. if ($mview->special_index) {
  410. $rows[] = array('Special Indexed Fields', $mview->special_index);
  411. }
  412. if ($mview->mv_schema) {
  413. $rows[] = array('Drupal Schema API Definition', "<pre>" . $mview->mv_schema . "</pre>");
  414. }
  415. $table = theme_table(array(), $rows);
  416. $output .= $table;
  417. return $output;
  418. }
  419. /**
  420. * A template function to render a listing of all Materialized Views
  421. *
  422. * @ingroup tripal_mviews_api
  423. */
  424. function tripal_mviews_report() {
  425. $header = array('', 'MView Name', 'Last Update', 'Status', 'Description', '');
  426. $rows = array();
  427. $mviews = db_query("SELECT * FROM {tripal_mviews} ORDER BY name");
  428. while ($mview = db_fetch_object($mviews)) {
  429. if ($mview->last_update > 0) {
  430. $update = format_date($mview->last_update);
  431. }
  432. else {
  433. $update = 'Not yet populated';
  434. }
  435. $rows[] = array(
  436. l(t('View'), "admin/tripal/mviews/report/$mview->mview_id") ." | ".
  437. l(t('Edit'), "admin/tripal/mviews/edit/$mview->mview_id") ." | ".
  438. l(t('Populate'), "admin/tripal/mviews/action/update/$mview->mview_id"),
  439. $mview->name,
  440. $update,
  441. $mview->status,
  442. $mview->comment,
  443. l(t('Delete'), "admin/tripal/mviews/action/delete/$mview->mview_id"),
  444. );
  445. }
  446. $rows[] = array(
  447. 'data' => array(
  448. array('data' => l(t('Create a new materialized view.'), "admin/tripal/mviews/new"),
  449. 'colspan' => 6),
  450. )
  451. );
  452. $page = '</p>' . t("Materialized Views (MViews) are custom tables populated with a defined SQL statement.
  453. Because Chado is highly normalized and highly constrained it serves as a wonderful
  454. data storage platform, but unfortunately some queries may be slow. MViews alleviate slowness by aggregating data
  455. into tables that are more easy to query. Use MViews to create tables for custom search pages or custom Tripal
  456. module development.") . '</p>';
  457. $page .= '<p><b>' . t("MViews behaves in the following way:") . '</b><ul>'.
  458. '<li>' . t("The SQL statement defined for an MVIEW will be used to populate the table") . '</li>' .
  459. '<li>' . t("Altering the table structure of an MView will cause the MView table to be dropped and recreated. All records in the MView will be lost.") . '</li>' .
  460. '<li>' . t("Altering the query of an existing view will not change the MView table. No records will be lost. ") . '</li>' .
  461. '<li>' . t("Repopulating an MView that is already populated will result in replacement of all records.") . '</li>' .
  462. '<li>' . t("A database transaction will be used when populating MViews. Therefore replacement of records does not occur until the query completes. Any search forms or pages dependent on the MView will continue to function.") . '</li>' .
  463. '</ul></p>';
  464. $page .= '<b>' . t("Existing MViews") . '</b>';
  465. $page .= theme('table', $header, $rows);
  466. return $page;
  467. }
  468. /**
  469. * A Form to Create/Edit a Materialized View
  470. *
  471. * @param $form_state
  472. * The current state of the form (Form API)
  473. * @param $mview_id
  474. * The unique ID of the Materialized View to Edit or NULL if creating a new materialized view
  475. *
  476. * @return
  477. * A form array (Form API)
  478. *
  479. * @ingroup tripal_core
  480. */
  481. function tripal_mviews_form(&$form_state = NULL, $mview_id = NULL) {
  482. if (!$mview_id) {
  483. $action = 'Add';
  484. }
  485. else {
  486. $action = 'Edit';
  487. }
  488. // set defaults for collapsed fieldsets
  489. $schema_collapsed = 0;
  490. $traditional_collapsed = 1;
  491. // get this requested view
  492. if (strcmp($action, 'Edit')==0) {
  493. $sql = "SELECT * FROM {tripal_mviews} WHERE mview_id = %d ";
  494. $mview = db_fetch_object(db_query($sql, $mview_id));
  495. // set the default values. If there is a value set in the
  496. // form_state then let's use that, otherwise, we'll pull
  497. // the values from the database
  498. $default_name = $form_state['values']['name'];
  499. $default_mv_table = $form_state['values']['mv_table'];
  500. $default_mv_specs = $form_state['values']['mv_specs'];
  501. $default_indexed = $form_state['values']['indexed'];
  502. $default_mvquery = $form_state['values']['mvquery'];
  503. $default_special_index = $form_state['values']['special_index'];
  504. $default_comment = $form_state['values']['comment'];
  505. $default_modulename = $form_state['values']['modulename'];
  506. if (!$default_name) {
  507. $default_name = $mview->name;
  508. }
  509. if (!$default_mv_table) {
  510. $default_mv_table = $mview->mv_table;
  511. }
  512. if (!$default_mv_specs) {
  513. $default_mv_specs = $mview->mv_specs;
  514. }
  515. if (!$default_indexed) {
  516. $default_indexed = $mview->indexed;
  517. }
  518. if (!$default_mvquery) {
  519. $default_mvquery = $mview->query;
  520. }
  521. if (!$default_special_index) {
  522. $default_special_index = $mview->special_index;
  523. }
  524. if (!$default_comment) {
  525. $default_comment = $mview->comment;
  526. }
  527. if (!$default_schema) {
  528. $default_schema = $mview->mv_schema;
  529. }
  530. if (!$default_modulename) {
  531. $default_modulename = $mview->modulename ? $mview->modulename : 'tripal_core';
  532. }
  533. // the mv_table column of the tripal_mviews table always has the table
  534. // name even if it is a custom table. However, for the sake of the form,
  535. // we do not want this to show up as the mv_table is needed for the
  536. // traditional style input. We'll blank it out if we have a custom
  537. // table and it will get reset in the submit function using the
  538. // 'table' value from the schema array
  539. if ($default_schema) {
  540. $default_mv_table = '';
  541. }
  542. // set which fieldset is collapsed
  543. if (!$default_schema) {
  544. $schema_collapsed = 1;
  545. $traditional_collapsed = 0;
  546. }
  547. }
  548. // Build the form
  549. $form['action'] = array(
  550. '#type' => 'value',
  551. '#value' => $action
  552. );
  553. $form['mview_id'] = array(
  554. '#type' => 'value',
  555. '#value' => $mview_id
  556. );
  557. $form['modulename'] = array(
  558. '#type' => 'value',
  559. '#value' => $default_modulename,
  560. );
  561. $form['name']= array(
  562. '#type' => 'textfield',
  563. '#title' => t('View Name'),
  564. '#description' => t('Please enter the name for this materialized view.'),
  565. '#required' => TRUE,
  566. '#default_value' => $default_name,
  567. );
  568. $form['comment']= array(
  569. '#type' => 'textarea',
  570. '#title' => t('MView Description'),
  571. '#description' => t('Optional. Please provide a description of the purpose for this materialized vieww.'),
  572. '#required' => FALSE,
  573. '#default_value' => $default_comment,
  574. );
  575. // add a fieldset for the Drupal Schema API
  576. $form['schema'] = array(
  577. '#type' => 'fieldset',
  578. '#title' => 'Drupal Schema API Setup',
  579. '#description' => t('Use the Drupal Schema API array to describe a table. The benefit is that it '.
  580. 'can be fully integrated with Tripal Views. Tripal supports an extended '.
  581. 'array format to allow for descriptoin of foreign key relationships.'),
  582. '#collapsible' => 1,
  583. '#collapsed' => $schema_collapsed ,
  584. );
  585. $form['schema']['schema']= array(
  586. '#type' => 'textarea',
  587. '#title' => t('Schema Array'),
  588. '#description' => t('Please enter the Drupal Schema API compatible array that defines the table.'),
  589. '#required' => FALSE,
  590. '#default_value' => $default_schema,
  591. '#rows' => 25,
  592. );
  593. // add a fieldset for the Original Table Description fields
  594. $form['traditional'] = array(
  595. '#type' => 'fieldset',
  596. '#title' => 'Legacy MViews Setup',
  597. '#description' => t('Traditionally MViews were created by specifying PostgreSQL style '.
  598. 'column types. This method can be used but is deprecated in favor of the '.
  599. 'newer Drupal schema API method provided above. In rare cases where the Drupal Schema API ' .
  600. 'does not support a desired data type the Legacy Mviews should be used'),
  601. '#collapsible' => 1,
  602. '#collapsed' => $traditional_collapsed,
  603. );
  604. $form['traditional']['mv_table']= array(
  605. '#type' => 'textfield',
  606. '#title' => t('Table Name'),
  607. '#description' => t('Please enter the table name that this view will generate in the database. You can use the schema and table name for querying the view'),
  608. '#required' => FALSE,
  609. '#default_value' => $default_mv_table,
  610. );
  611. $form['traditional']['mv_specs']= array(
  612. '#type' => 'textarea',
  613. '#title' => t('Table Definition'),
  614. '#description' => t('Please enter the field definitions for this view. Each field should be separated by a comma or enter each field definition on each line.'),
  615. '#required' => FALSE,
  616. '#default_value' => $default_mv_specs,
  617. );
  618. $form['traditional']['indexed']= array(
  619. '#type' => 'textarea',
  620. '#title' => t('Indexed Fields'),
  621. '#description' => t('Please enter the field names (as provided in the table definition above) that will be indexed for this view. Separate by a comma or enter each field on a new line.'),
  622. '#required' => FALSE,
  623. '#default_value' => $default_indexed,
  624. );
  625. /**
  626. $form['traditional']['special_index']= array(
  627. '#type' => 'textarea',
  628. '#title' => t('View Name'),
  629. '#description' => t('Please enter the name for this materialized view.'),
  630. '#required' => TRUE,
  631. '#default_value' => $default_special_index,
  632. );
  633. */
  634. $form['mvquery']= array(
  635. '#type' => 'textarea',
  636. '#title' => t('Query'),
  637. '#description' => t('Please enter the SQL statement used to populate the table.'),
  638. '#required' => TRUE,
  639. '#default_value' => $default_mvquery,
  640. '#rows' => 25,
  641. );
  642. if ($action == 'Edit') {
  643. $value = 'Save';
  644. }
  645. if ($action == 'Add') {
  646. $value = 'Add';
  647. }
  648. $form['submit'] = array(
  649. '#type' => 'submit',
  650. '#value' => t($value),
  651. '#weight' => 9,
  652. '#executes_submit_callback' => TRUE,
  653. );
  654. $form['#redirect'] = 'admin/tripal/mviews';
  655. return $form;
  656. }
  657. /**
  658. * Validate the Create/Edit Materialized View Form
  659. * Implements hook_form_validate().
  660. *
  661. * @ingroup tripal_core
  662. */
  663. function tripal_mviews_form_validate($form, &$form_state) {
  664. $action = $form_state['values']['action'];
  665. $mview_id = $form_state['values']['mview_id'];
  666. $name = $form_state['values']['name'];
  667. $mv_table = $form_state['values']['mv_table'];
  668. $mv_specs = $form_state['values']['mv_specs'];
  669. $indexed = $form_state['values']['indexed'];
  670. $query = $form_state['values']['mvquery'];
  671. $special_index = $form_state['values']['special_index'];
  672. $comment = $form_state['values']['comment'];
  673. $schema = $form_state['values']['schema'];
  674. if ($schema and ($mv_table or $mv_specs or $indexed or $special_index)) {
  675. form_set_error($form_state['values']['schema'],
  676. t('You can create an MView using the Drupal Schema API method or the '.
  677. 'traditional method but not both.'));
  678. }
  679. if (!$schema) {
  680. if (!$mv_specs) {
  681. form_set_error($form_state['values']['mv_specs'],
  682. t('The Table Definition field is required.'));
  683. }
  684. if (!$mv_table) {
  685. form_set_error($form_state['values']['mv_table'],
  686. t('The Table Name field is required.'));
  687. }
  688. }
  689. // make sure the array is valid
  690. if ($schema) {
  691. $success = eval("\$schema_array = $schema;");
  692. if ($success === FALSE) {
  693. $error = error_get_last();
  694. form_set_error($form_state['values']['schema'],
  695. t("The schema array is improperly formatted. Parse Error : " . $error["message"]));
  696. }
  697. if (!array_key_exists('table', $schema_array)) {
  698. form_set_error($form_state['values']['schema'],
  699. t("The schema array must have key named 'table'"));
  700. }
  701. // TODO: add in more validation checks of the array to help the user
  702. }
  703. }
  704. /**
  705. * Submit the Create/Edit Materialized View Form
  706. * Implements hook_form_submit().
  707. *
  708. * @ingroup tripal_core
  709. */
  710. function tripal_mviews_form_submit($form, &$form_state) {
  711. $ret = array();
  712. $action = $form_state['values']['action'];
  713. $mview_id = $form_state['values']['mview_id'];
  714. $name = $form_state['values']['name'];
  715. $mv_table = $form_state['values']['mv_table'];
  716. $mv_specs = $form_state['values']['mv_specs'];
  717. $indexed = $form_state['values']['indexed'];
  718. $query = $form_state['values']['mvquery'];
  719. $special_index = $form_state['values']['special_index'];
  720. $comment = $form_state['values']['comment'];
  721. $schema = $form_state['values']['schema'];
  722. $modulename = $form_state['values']['modulename'];
  723. if (strcmp($action, 'Edit') == 0) {
  724. tripal_edit_mview($mview_id, $name, $modulename, $mv_table, $mv_specs,
  725. $indexed, $query, $special_index, $comment, $schema);
  726. }
  727. elseif (strcmp($action, 'Add') == 0) {
  728. tripal_add_mview($name, $modulename, $mv_table, $mv_specs,
  729. $indexed, $query, $special_index, $comment, $schema);
  730. }
  731. else {
  732. drupal_set_message(t("No action performed."));
  733. }
  734. return '';
  735. }