tbl_replace.php 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485
  1. <?php
  2. /* vim: set expandtab sw=4 ts=4 sts=4: */
  3. /**
  4. * Manipulation of table data like inserting, replacing and updating
  5. *
  6. * Usually called as form action from tbl_change.php to insert or update table rows
  7. *
  8. * @todo 'edit_next' tends to not work as expected if used ...
  9. * at least there is no order by it needs the original query
  10. * and the row number and than replace the LIMIT clause
  11. *
  12. * @package PhpMyAdmin
  13. */
  14. use PhpMyAdmin\Core;
  15. use PhpMyAdmin\File;
  16. use PhpMyAdmin\InsertEdit;
  17. use PhpMyAdmin\Message;
  18. use PhpMyAdmin\Plugins\IOTransformationsPlugin;
  19. use PhpMyAdmin\Relation;
  20. use PhpMyAdmin\Response;
  21. use PhpMyAdmin\Table;
  22. use PhpMyAdmin\Transformations;
  23. use PhpMyAdmin\Util;
  24. /**
  25. * Gets some core libraries
  26. */
  27. require_once 'libraries/common.inc.php';
  28. // Check parameters
  29. Util::checkParameters(array('db', 'table', 'goto'));
  30. $GLOBALS['dbi']->selectDb($GLOBALS['db']);
  31. /**
  32. * Initializes some variables
  33. */
  34. $goto_include = false;
  35. $response = Response::getInstance();
  36. $header = $response->getHeader();
  37. $scripts = $header->getScripts();
  38. $scripts->addFile('makegrid.js');
  39. // Needed for generation of Inline Edit anchors
  40. $scripts->addFile('sql.js');
  41. $scripts->addFile('indexes.js');
  42. $scripts->addFile('gis_data_editor.js');
  43. $relation = new Relation();
  44. $insertEdit = new InsertEdit($GLOBALS['dbi']);
  45. // check whether insert row mode, if so include tbl_change.php
  46. $insertEdit->isInsertRow();
  47. $after_insert_actions = array('new_insert', 'same_insert', 'edit_next');
  48. if (isset($_POST['after_insert'])
  49. && in_array($_POST['after_insert'], $after_insert_actions)
  50. ) {
  51. $url_params['after_insert'] = $_POST['after_insert'];
  52. if (isset($_POST['where_clause'])) {
  53. foreach ($_POST['where_clause'] as $one_where_clause) {
  54. if ($_POST['after_insert'] == 'same_insert') {
  55. $url_params['where_clause'][] = $one_where_clause;
  56. } elseif ($_POST['after_insert'] == 'edit_next') {
  57. $insertEdit->setSessionForEditNext($one_where_clause);
  58. }
  59. }
  60. }
  61. }
  62. //get $goto_include for different cases
  63. $goto_include = $insertEdit->getGotoInclude($goto_include);
  64. // Defines the url to return in case of failure of the query
  65. $err_url = $insertEdit->getErrorUrl($url_params);
  66. /**
  67. * Prepares the update/insert of a row
  68. */
  69. list($loop_array, $using_key, $is_insert, $is_insertignore)
  70. = $insertEdit->getParamsForUpdateOrInsert();
  71. $query = array();
  72. $value_sets = array();
  73. $func_no_param = array(
  74. 'CONNECTION_ID',
  75. 'CURRENT_USER',
  76. 'CURDATE',
  77. 'CURTIME',
  78. 'CURRENT_DATE',
  79. 'CURRENT_TIME',
  80. 'DATABASE',
  81. 'LAST_INSERT_ID',
  82. 'NOW',
  83. 'PI',
  84. 'RAND',
  85. 'SYSDATE',
  86. 'UNIX_TIMESTAMP',
  87. 'USER',
  88. 'UTC_DATE',
  89. 'UTC_TIME',
  90. 'UTC_TIMESTAMP',
  91. 'UUID',
  92. 'UUID_SHORT',
  93. 'VERSION',
  94. );
  95. $func_optional_param = array(
  96. 'RAND',
  97. 'UNIX_TIMESTAMP',
  98. );
  99. $gis_from_text_functions = array(
  100. 'GeomFromText',
  101. 'GeomCollFromText',
  102. 'LineFromText',
  103. 'MLineFromText',
  104. 'PointFromText',
  105. 'MPointFromText',
  106. 'PolyFromText',
  107. 'MPolyFromText',
  108. );
  109. $gis_from_wkb_functions = array(
  110. 'GeomFromWKB',
  111. 'GeomCollFromWKB',
  112. 'LineFromWKB',
  113. 'MLineFromWKB',
  114. 'PointFromWKB',
  115. 'MPointFromWKB',
  116. 'PolyFromWKB',
  117. 'MPolyFromWKB',
  118. );
  119. //if some posted fields need to be transformed.
  120. $mime_map = Transformations::getMIME($GLOBALS['db'], $GLOBALS['table']);
  121. if ($mime_map === false) {
  122. $mime_map = array();
  123. }
  124. $query_fields = array();
  125. $insert_errors = array();
  126. $row_skipped = false;
  127. $unsaved_values = array();
  128. foreach ($loop_array as $rownumber => $where_clause) {
  129. // skip fields to be ignored
  130. if (! $using_key && isset($_POST['insert_ignore_' . $where_clause])) {
  131. continue;
  132. }
  133. // Defines the SET part of the sql query
  134. $query_values = array();
  135. // Map multi-edit keys to single-level arrays, dependent on how we got the fields
  136. $multi_edit_columns
  137. = isset($_POST['fields']['multi_edit'][$rownumber])
  138. ? $_POST['fields']['multi_edit'][$rownumber]
  139. : array();
  140. $multi_edit_columns_name
  141. = isset($_POST['fields_name']['multi_edit'][$rownumber])
  142. ? $_POST['fields_name']['multi_edit'][$rownumber]
  143. : array();
  144. $multi_edit_columns_prev
  145. = isset($_POST['fields_prev']['multi_edit'][$rownumber])
  146. ? $_POST['fields_prev']['multi_edit'][$rownumber]
  147. : null;
  148. $multi_edit_funcs
  149. = isset($_POST['funcs']['multi_edit'][$rownumber])
  150. ? $_POST['funcs']['multi_edit'][$rownumber]
  151. : null;
  152. $multi_edit_salt
  153. = isset($_POST['salt']['multi_edit'][$rownumber])
  154. ? $_POST['salt']['multi_edit'][$rownumber]
  155. :null;
  156. $multi_edit_columns_type
  157. = isset($_POST['fields_type']['multi_edit'][$rownumber])
  158. ? $_POST['fields_type']['multi_edit'][$rownumber]
  159. : null;
  160. $multi_edit_columns_null
  161. = isset($_POST['fields_null']['multi_edit'][$rownumber])
  162. ? $_POST['fields_null']['multi_edit'][$rownumber]
  163. : null;
  164. $multi_edit_columns_null_prev
  165. = isset($_POST['fields_null_prev']['multi_edit'][$rownumber])
  166. ? $_POST['fields_null_prev']['multi_edit'][$rownumber]
  167. : null;
  168. $multi_edit_auto_increment
  169. = isset($_POST['auto_increment']['multi_edit'][$rownumber])
  170. ? $_POST['auto_increment']['multi_edit'][$rownumber]
  171. : null;
  172. $multi_edit_virtual
  173. = isset($_POST['virtual']['multi_edit'][$rownumber])
  174. ? $_POST['virtual']['multi_edit'][$rownumber]
  175. : null;
  176. // When a select field is nullified, it's not present in $_POST
  177. // so initialize it; this way, the foreach($multi_edit_columns) will process it
  178. foreach ($multi_edit_columns_name as $key => $val) {
  179. if (! isset($multi_edit_columns[$key])) {
  180. $multi_edit_columns[$key] = '';
  181. }
  182. }
  183. // Iterate in the order of $multi_edit_columns_name,
  184. // not $multi_edit_columns, to avoid problems
  185. // when inserting multiple entries
  186. $insert_fail = false;
  187. foreach ($multi_edit_columns_name as $key => $column_name) {
  188. $current_value = $multi_edit_columns[$key];
  189. // Note: $key is an md5 of the fieldname. The actual fieldname is
  190. // available in $multi_edit_columns_name[$key]
  191. $file_to_insert = new File();
  192. $file_to_insert->checkTblChangeForm($key, $rownumber);
  193. $possibly_uploaded_val = $file_to_insert->getContent();
  194. if ($possibly_uploaded_val !== false) {
  195. $current_value = $possibly_uploaded_val;
  196. }
  197. // Apply Input Transformation if defined
  198. if (!empty($mime_map[$column_name])
  199. && !empty($mime_map[$column_name]['input_transformation'])
  200. ) {
  201. $filename = 'libraries/classes/Plugins/Transformations/'
  202. . $mime_map[$column_name]['input_transformation'];
  203. if (is_file($filename)) {
  204. $classname = Transformations::getClassName($filename);
  205. if (class_exists($classname)) {
  206. /** @var IOTransformationsPlugin $transformation_plugin */
  207. $transformation_plugin = new $classname();
  208. $transformation_options = Transformations::getOptions(
  209. $mime_map[$column_name]['input_transformation_options']
  210. );
  211. $current_value = $transformation_plugin->applyTransformation(
  212. $current_value, $transformation_options
  213. );
  214. // check if transformation was successful or not
  215. // and accordingly set error messages & insert_fail
  216. if (method_exists($transformation_plugin, 'isSuccess')
  217. && !$transformation_plugin->isSuccess()
  218. ) {
  219. $insert_fail = true;
  220. $row_skipped = true;
  221. $insert_errors[] = sprintf(
  222. __('Row: %1$s, Column: %2$s, Error: %3$s'),
  223. $rownumber, $column_name,
  224. $transformation_plugin->getError()
  225. );
  226. }
  227. }
  228. }
  229. }
  230. if ($file_to_insert->isError()) {
  231. $insert_errors[] = $file_to_insert->getError();
  232. }
  233. // delete $file_to_insert temporary variable
  234. $file_to_insert->cleanUp();
  235. $current_value = $insertEdit->getCurrentValueForDifferentTypes(
  236. $possibly_uploaded_val, $key, $multi_edit_columns_type,
  237. $current_value, $multi_edit_auto_increment,
  238. $rownumber, $multi_edit_columns_name, $multi_edit_columns_null,
  239. $multi_edit_columns_null_prev, $is_insert,
  240. $using_key, $where_clause, $table, $multi_edit_funcs
  241. );
  242. $current_value_as_an_array = $insertEdit->getCurrentValueAsAnArrayForMultipleEdit(
  243. $multi_edit_funcs,
  244. $multi_edit_salt, $gis_from_text_functions, $current_value,
  245. $gis_from_wkb_functions, $func_optional_param, $func_no_param, $key
  246. );
  247. if (! isset($multi_edit_virtual) || ! isset($multi_edit_virtual[$key])) {
  248. list($query_values, $query_fields)
  249. = $insertEdit->getQueryValuesForInsertAndUpdateInMultipleEdit(
  250. $multi_edit_columns_name, $multi_edit_columns_null,
  251. $current_value, $multi_edit_columns_prev, $multi_edit_funcs,
  252. $is_insert, $query_values, $query_fields,
  253. $current_value_as_an_array, $value_sets, $key,
  254. $multi_edit_columns_null_prev
  255. );
  256. }
  257. if (isset($multi_edit_columns_null[$key])) {
  258. $multi_edit_columns[$key] = null;
  259. }
  260. } //end of foreach
  261. // temporarily store rows not inserted
  262. // so that they can be populated again.
  263. if ($insert_fail) {
  264. $unsaved_values[$rownumber] = $multi_edit_columns;
  265. }
  266. if (!$insert_fail && count($query_values) > 0) {
  267. if ($is_insert) {
  268. $value_sets[] = implode(', ', $query_values);
  269. } else {
  270. // build update query
  271. $query[] = 'UPDATE ' . Util::backquote($GLOBALS['table'])
  272. . ' SET ' . implode(', ', $query_values)
  273. . ' WHERE ' . $where_clause
  274. . ($_POST['clause_is_unique'] ? '' : ' LIMIT 1');
  275. }
  276. }
  277. } // end foreach ($loop_array as $where_clause)
  278. unset(
  279. $multi_edit_columns_name, $multi_edit_columns_prev, $multi_edit_funcs,
  280. $multi_edit_columns_type, $multi_edit_columns_null, $func_no_param,
  281. $multi_edit_auto_increment, $current_value_as_an_array, $key, $current_value,
  282. $loop_array, $where_clause, $using_key, $multi_edit_columns_null_prev,
  283. $insert_fail
  284. );
  285. // Builds the sql query
  286. if ($is_insert && count($value_sets) > 0) {
  287. $query = $insertEdit->buildSqlQuery($is_insertignore, $query_fields, $value_sets);
  288. } elseif (empty($query) && ! isset($_POST['preview_sql']) && !$row_skipped) {
  289. // No change -> move back to the calling script
  290. //
  291. // Note: logic passes here for inline edit
  292. $message = Message::success(__('No change'));
  293. // Avoid infinite recursion
  294. if ($goto_include == 'tbl_replace.php') {
  295. $goto_include = 'tbl_change.php';
  296. }
  297. $active_page = $goto_include;
  298. include '' . Core::securePath($goto_include);
  299. exit;
  300. }
  301. unset($multi_edit_columns, $is_insertignore);
  302. // If there is a request for SQL previewing.
  303. if (isset($_POST['preview_sql'])) {
  304. Core::previewSQL($query);
  305. }
  306. /**
  307. * Executes the sql query and get the result, then move back to the calling
  308. * page
  309. */
  310. list ($url_params, $total_affected_rows, $last_messages, $warning_messages,
  311. $error_messages, $return_to_sql_query)
  312. = $insertEdit->executeSqlQuery($url_params, $query);
  313. if ($is_insert && (count($value_sets) > 0 || $row_skipped)) {
  314. $message = Message::getMessageForInsertedRows(
  315. $total_affected_rows
  316. );
  317. $unsaved_values = array_values($unsaved_values);
  318. } else {
  319. $message = Message::getMessageForAffectedRows(
  320. $total_affected_rows
  321. );
  322. }
  323. if ($row_skipped) {
  324. $goto_include = 'tbl_change.php';
  325. $message->addMessagesString($insert_errors, '<br />');
  326. $message->isError(true);
  327. }
  328. $message->addMessages($last_messages, '<br />');
  329. if (! empty($warning_messages)) {
  330. $message->addMessagesString($warning_messages, '<br />');
  331. $message->isError(true);
  332. }
  333. if (! empty($error_messages)) {
  334. $message->addMessagesString($error_messages);
  335. $message->isError(true);
  336. }
  337. unset(
  338. $error_messages, $warning_messages, $total_affected_rows,
  339. $last_messages, $last_message, $row_skipped, $insert_errors
  340. );
  341. /**
  342. * The following section only applies to grid editing.
  343. * However, verifying isAjax() is not enough to ensure we are coming from
  344. * grid editing. If we are coming from the Edit or Copy link in Browse mode,
  345. * ajax_page_request is present in the POST parameters.
  346. */
  347. if ($response->isAjax() && ! isset($_POST['ajax_page_request'])) {
  348. /**
  349. * If we are in grid editing, we need to process the relational and
  350. * transformed fields, if they were edited. After that, output the correct
  351. * link/transformed value and exit
  352. */
  353. if (isset($_POST['rel_fields_list']) && $_POST['rel_fields_list'] != '') {
  354. $map = $relation->getForeigners($db, $table, '', 'both');
  355. $relation_fields = array();
  356. parse_str($_POST['rel_fields_list'], $relation_fields);
  357. // loop for each relation cell
  358. /** @var array $relation_fields */
  359. foreach ($relation_fields as $cell_index => $curr_rel_field) {
  360. foreach ($curr_rel_field as $relation_field => $relation_field_value) {
  361. $where_comparison = "='" . $relation_field_value . "'";
  362. $dispval = $insertEdit->getDisplayValueForForeignTableColumn(
  363. $where_comparison, $map, $relation_field
  364. );
  365. $extra_data['relations'][$cell_index]
  366. = $insertEdit->getLinkForRelationalDisplayField(
  367. $map, $relation_field, $where_comparison,
  368. $dispval, $relation_field_value
  369. );
  370. }
  371. } // end of loop for each relation cell
  372. }
  373. if (isset($_POST['do_transformations'])
  374. && $_POST['do_transformations'] == true
  375. ) {
  376. $edited_values = array();
  377. parse_str($_POST['transform_fields_list'], $edited_values);
  378. if (! isset($extra_data)) {
  379. $extra_data = array();
  380. }
  381. $transformation_types = array(
  382. "input_transformation",
  383. "transformation"
  384. );
  385. foreach ($mime_map as $transformation) {
  386. $column_name = $transformation['column_name'];
  387. foreach ($transformation_types as $type) {
  388. $file = Core::securePath($transformation[$type]);
  389. $extra_data = $insertEdit->transformEditedValues(
  390. $db, $table, $transformation, $edited_values, $file,
  391. $column_name, $extra_data, $type
  392. );
  393. }
  394. } // end of loop for each $mime_map
  395. }
  396. // Need to check the inline edited value can be truncated by MySQL
  397. // without informing while saving
  398. $column_name = $_POST['fields_name']['multi_edit'][0][0];
  399. $insertEdit->verifyWhetherValueCanBeTruncatedAndAppendExtraData(
  400. $db, $table, $column_name, $extra_data
  401. );
  402. /**Get the total row count of the table*/
  403. $_table = new Table($_POST['table'], $_POST['db']);
  404. $extra_data['row_count'] = $_table->countRecords();
  405. $extra_data['sql_query'] = Util::getMessage(
  406. $message,
  407. $GLOBALS['display_query']
  408. );
  409. $response->setRequestStatus($message->isSuccess());
  410. $response->addJSON('message', $message);
  411. $response->addJSON($extra_data);
  412. exit;
  413. }
  414. if (! empty($return_to_sql_query)) {
  415. $disp_query = $GLOBALS['sql_query'];
  416. $disp_message = $message;
  417. unset($message);
  418. $GLOBALS['sql_query'] = $return_to_sql_query;
  419. }
  420. $scripts->addFile('vendor/jquery/additional-methods.js');
  421. $scripts->addFile('tbl_change.js');
  422. $active_page = $goto_include;
  423. /**
  424. * If user asked for "and then Insert another new row" we have to remove
  425. * WHERE clause information so that tbl_change.php does not go back
  426. * to the current record
  427. */
  428. if (isset($_POST['after_insert']) && 'new_insert' == $_POST['after_insert']) {
  429. unset($_POST['where_clause']);
  430. }
  431. /**
  432. * Load target page.
  433. */
  434. require '' . Core::securePath($goto_include);
  435. exit;