tbl_replace.php 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498
  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. if ($GLOBALS['dbi']->getVersion() >= 50600) {
  110. $gis_from_text_functions = array(
  111. 'ST_GeomFromText',
  112. 'ST_GeomCollFromText',
  113. 'ST_LineFromText',
  114. 'ST_MLineFromText',
  115. 'ST_PointFromText',
  116. 'ST_MPointFromText',
  117. 'ST_PolyFromText',
  118. 'ST_MPolyFromText',
  119. );
  120. }
  121. $gis_from_wkb_functions = array(
  122. 'GeomFromWKB',
  123. 'GeomCollFromWKB',
  124. 'LineFromWKB',
  125. 'MLineFromWKB',
  126. 'PointFromWKB',
  127. 'MPointFromWKB',
  128. 'PolyFromWKB',
  129. 'MPolyFromWKB',
  130. );
  131. //if some posted fields need to be transformed.
  132. $mime_map = Transformations::getMIME($GLOBALS['db'], $GLOBALS['table']);
  133. if ($mime_map === false) {
  134. $mime_map = array();
  135. }
  136. $query_fields = array();
  137. $insert_errors = array();
  138. $row_skipped = false;
  139. $unsaved_values = array();
  140. foreach ($loop_array as $rownumber => $where_clause) {
  141. // skip fields to be ignored
  142. if (! $using_key && isset($_POST['insert_ignore_' . $where_clause])) {
  143. continue;
  144. }
  145. // Defines the SET part of the sql query
  146. $query_values = array();
  147. // Map multi-edit keys to single-level arrays, dependent on how we got the fields
  148. $multi_edit_columns
  149. = isset($_POST['fields']['multi_edit'][$rownumber])
  150. ? $_POST['fields']['multi_edit'][$rownumber]
  151. : array();
  152. $multi_edit_columns_name
  153. = isset($_POST['fields_name']['multi_edit'][$rownumber])
  154. ? $_POST['fields_name']['multi_edit'][$rownumber]
  155. : array();
  156. $multi_edit_columns_prev
  157. = isset($_POST['fields_prev']['multi_edit'][$rownumber])
  158. ? $_POST['fields_prev']['multi_edit'][$rownumber]
  159. : null;
  160. $multi_edit_funcs
  161. = isset($_POST['funcs']['multi_edit'][$rownumber])
  162. ? $_POST['funcs']['multi_edit'][$rownumber]
  163. : null;
  164. $multi_edit_salt
  165. = isset($_POST['salt']['multi_edit'][$rownumber])
  166. ? $_POST['salt']['multi_edit'][$rownumber]
  167. :null;
  168. $multi_edit_columns_type
  169. = isset($_POST['fields_type']['multi_edit'][$rownumber])
  170. ? $_POST['fields_type']['multi_edit'][$rownumber]
  171. : null;
  172. $multi_edit_columns_null
  173. = isset($_POST['fields_null']['multi_edit'][$rownumber])
  174. ? $_POST['fields_null']['multi_edit'][$rownumber]
  175. : null;
  176. $multi_edit_columns_null_prev
  177. = isset($_POST['fields_null_prev']['multi_edit'][$rownumber])
  178. ? $_POST['fields_null_prev']['multi_edit'][$rownumber]
  179. : null;
  180. $multi_edit_auto_increment
  181. = isset($_POST['auto_increment']['multi_edit'][$rownumber])
  182. ? $_POST['auto_increment']['multi_edit'][$rownumber]
  183. : null;
  184. $multi_edit_virtual
  185. = isset($_POST['virtual']['multi_edit'][$rownumber])
  186. ? $_POST['virtual']['multi_edit'][$rownumber]
  187. : null;
  188. // When a select field is nullified, it's not present in $_POST
  189. // so initialize it; this way, the foreach($multi_edit_columns) will process it
  190. foreach ($multi_edit_columns_name as $key => $val) {
  191. if (! isset($multi_edit_columns[$key])) {
  192. $multi_edit_columns[$key] = '';
  193. }
  194. }
  195. // Iterate in the order of $multi_edit_columns_name,
  196. // not $multi_edit_columns, to avoid problems
  197. // when inserting multiple entries
  198. $insert_fail = false;
  199. foreach ($multi_edit_columns_name as $key => $column_name) {
  200. $current_value = $multi_edit_columns[$key];
  201. // Note: $key is an md5 of the fieldname. The actual fieldname is
  202. // available in $multi_edit_columns_name[$key]
  203. $file_to_insert = new File();
  204. $file_to_insert->checkTblChangeForm($key, $rownumber);
  205. $possibly_uploaded_val = $file_to_insert->getContent();
  206. if ($possibly_uploaded_val !== false) {
  207. $current_value = $possibly_uploaded_val;
  208. }
  209. // Apply Input Transformation if defined
  210. if (!empty($mime_map[$column_name])
  211. && !empty($mime_map[$column_name]['input_transformation'])
  212. ) {
  213. $filename = 'libraries/classes/Plugins/Transformations/'
  214. . $mime_map[$column_name]['input_transformation'];
  215. if (is_file($filename)) {
  216. $classname = Transformations::getClassName($filename);
  217. if (class_exists($classname)) {
  218. /** @var IOTransformationsPlugin $transformation_plugin */
  219. $transformation_plugin = new $classname();
  220. $transformation_options = Transformations::getOptions(
  221. $mime_map[$column_name]['input_transformation_options']
  222. );
  223. $current_value = $transformation_plugin->applyTransformation(
  224. $current_value, $transformation_options
  225. );
  226. // check if transformation was successful or not
  227. // and accordingly set error messages & insert_fail
  228. if (method_exists($transformation_plugin, 'isSuccess')
  229. && !$transformation_plugin->isSuccess()
  230. ) {
  231. $insert_fail = true;
  232. $row_skipped = true;
  233. $insert_errors[] = sprintf(
  234. __('Row: %1$s, Column: %2$s, Error: %3$s'),
  235. $rownumber, $column_name,
  236. $transformation_plugin->getError()
  237. );
  238. }
  239. }
  240. }
  241. }
  242. if ($file_to_insert->isError()) {
  243. $insert_errors[] = $file_to_insert->getError();
  244. }
  245. // delete $file_to_insert temporary variable
  246. $file_to_insert->cleanUp();
  247. $current_value = $insertEdit->getCurrentValueForDifferentTypes(
  248. $possibly_uploaded_val, $key, $multi_edit_columns_type,
  249. $current_value, $multi_edit_auto_increment,
  250. $rownumber, $multi_edit_columns_name, $multi_edit_columns_null,
  251. $multi_edit_columns_null_prev, $is_insert,
  252. $using_key, $where_clause, $table, $multi_edit_funcs
  253. );
  254. $current_value_as_an_array = $insertEdit->getCurrentValueAsAnArrayForMultipleEdit(
  255. $multi_edit_funcs,
  256. $multi_edit_salt, $gis_from_text_functions, $current_value,
  257. $gis_from_wkb_functions, $func_optional_param, $func_no_param, $key
  258. );
  259. if (! isset($multi_edit_virtual) || ! isset($multi_edit_virtual[$key])) {
  260. list($query_values, $query_fields)
  261. = $insertEdit->getQueryValuesForInsertAndUpdateInMultipleEdit(
  262. $multi_edit_columns_name, $multi_edit_columns_null,
  263. $current_value, $multi_edit_columns_prev, $multi_edit_funcs,
  264. $is_insert, $query_values, $query_fields,
  265. $current_value_as_an_array, $value_sets, $key,
  266. $multi_edit_columns_null_prev
  267. );
  268. }
  269. if (isset($multi_edit_columns_null[$key])) {
  270. $multi_edit_columns[$key] = null;
  271. }
  272. } //end of foreach
  273. // temporarily store rows not inserted
  274. // so that they can be populated again.
  275. if ($insert_fail) {
  276. $unsaved_values[$rownumber] = $multi_edit_columns;
  277. }
  278. if (!$insert_fail && count($query_values) > 0) {
  279. if ($is_insert) {
  280. $value_sets[] = implode(', ', $query_values);
  281. } else {
  282. // build update query
  283. $query[] = 'UPDATE ' . Util::backquote($GLOBALS['table'])
  284. . ' SET ' . implode(', ', $query_values)
  285. . ' WHERE ' . $where_clause
  286. . ($_POST['clause_is_unique'] ? '' : ' LIMIT 1');
  287. }
  288. }
  289. } // end foreach ($loop_array as $where_clause)
  290. unset(
  291. $multi_edit_columns_name, $multi_edit_columns_prev, $multi_edit_funcs,
  292. $multi_edit_columns_type, $multi_edit_columns_null, $func_no_param,
  293. $multi_edit_auto_increment, $current_value_as_an_array, $key, $current_value,
  294. $loop_array, $where_clause, $using_key, $multi_edit_columns_null_prev,
  295. $insert_fail
  296. );
  297. // Builds the sql query
  298. if ($is_insert && count($value_sets) > 0) {
  299. $query = $insertEdit->buildSqlQuery($is_insertignore, $query_fields, $value_sets);
  300. } elseif (empty($query) && ! isset($_POST['preview_sql']) && !$row_skipped) {
  301. // No change -> move back to the calling script
  302. //
  303. // Note: logic passes here for inline edit
  304. $message = Message::success(__('No change'));
  305. // Avoid infinite recursion
  306. if ($goto_include == 'tbl_replace.php') {
  307. $goto_include = 'tbl_change.php';
  308. }
  309. $active_page = $goto_include;
  310. include '' . Core::securePath($goto_include);
  311. exit;
  312. }
  313. unset($multi_edit_columns, $is_insertignore);
  314. // If there is a request for SQL previewing.
  315. if (isset($_POST['preview_sql'])) {
  316. Core::previewSQL($query);
  317. }
  318. /**
  319. * Executes the sql query and get the result, then move back to the calling
  320. * page
  321. */
  322. list ($url_params, $total_affected_rows, $last_messages, $warning_messages,
  323. $error_messages, $return_to_sql_query)
  324. = $insertEdit->executeSqlQuery($url_params, $query);
  325. if ($is_insert && (count($value_sets) > 0 || $row_skipped)) {
  326. $message = Message::getMessageForInsertedRows(
  327. $total_affected_rows
  328. );
  329. $unsaved_values = array_values($unsaved_values);
  330. } else {
  331. $message = Message::getMessageForAffectedRows(
  332. $total_affected_rows
  333. );
  334. }
  335. if ($row_skipped) {
  336. $goto_include = 'tbl_change.php';
  337. $message->addMessagesString($insert_errors, '<br />');
  338. $message->isError(true);
  339. }
  340. $message->addMessages($last_messages, '<br />');
  341. if (! empty($warning_messages)) {
  342. $message->addMessagesString($warning_messages, '<br />');
  343. $message->isError(true);
  344. }
  345. if (! empty($error_messages)) {
  346. $message->addMessagesString($error_messages);
  347. $message->isError(true);
  348. }
  349. unset(
  350. $error_messages, $warning_messages, $total_affected_rows,
  351. $last_messages, $last_message, $row_skipped, $insert_errors
  352. );
  353. /**
  354. * The following section only applies to grid editing.
  355. * However, verifying isAjax() is not enough to ensure we are coming from
  356. * grid editing. If we are coming from the Edit or Copy link in Browse mode,
  357. * ajax_page_request is present in the POST parameters.
  358. */
  359. if ($response->isAjax() && ! isset($_POST['ajax_page_request'])) {
  360. /**
  361. * If we are in grid editing, we need to process the relational and
  362. * transformed fields, if they were edited. After that, output the correct
  363. * link/transformed value and exit
  364. */
  365. if (isset($_POST['rel_fields_list']) && $_POST['rel_fields_list'] != '') {
  366. $map = $relation->getForeigners($db, $table, '', 'both');
  367. $relation_fields = array();
  368. parse_str($_POST['rel_fields_list'], $relation_fields);
  369. // loop for each relation cell
  370. /** @var array $relation_fields */
  371. foreach ($relation_fields as $cell_index => $curr_rel_field) {
  372. foreach ($curr_rel_field as $relation_field => $relation_field_value) {
  373. $where_comparison = "='" . $relation_field_value . "'";
  374. $dispval = $insertEdit->getDisplayValueForForeignTableColumn(
  375. $where_comparison, $map, $relation_field
  376. );
  377. $extra_data['relations'][$cell_index]
  378. = $insertEdit->getLinkForRelationalDisplayField(
  379. $map, $relation_field, $where_comparison,
  380. $dispval, $relation_field_value
  381. );
  382. }
  383. } // end of loop for each relation cell
  384. }
  385. if (isset($_POST['do_transformations'])
  386. && $_POST['do_transformations'] == true
  387. ) {
  388. $edited_values = array();
  389. parse_str($_POST['transform_fields_list'], $edited_values);
  390. if (! isset($extra_data)) {
  391. $extra_data = array();
  392. }
  393. $transformation_types = array(
  394. "input_transformation",
  395. "transformation"
  396. );
  397. foreach ($mime_map as $transformation) {
  398. $column_name = $transformation['column_name'];
  399. foreach ($transformation_types as $type) {
  400. $file = Core::securePath($transformation[$type]);
  401. $extra_data = $insertEdit->transformEditedValues(
  402. $db, $table, $transformation, $edited_values, $file,
  403. $column_name, $extra_data, $type
  404. );
  405. }
  406. } // end of loop for each $mime_map
  407. }
  408. // Need to check the inline edited value can be truncated by MySQL
  409. // without informing while saving
  410. $column_name = $_POST['fields_name']['multi_edit'][0][0];
  411. $insertEdit->verifyWhetherValueCanBeTruncatedAndAppendExtraData(
  412. $db, $table, $column_name, $extra_data
  413. );
  414. /**Get the total row count of the table*/
  415. $_table = new Table($_POST['table'], $_POST['db']);
  416. $extra_data['row_count'] = $_table->countRecords();
  417. $extra_data['sql_query'] = Util::getMessage(
  418. $message,
  419. $GLOBALS['display_query']
  420. );
  421. $response->setRequestStatus($message->isSuccess());
  422. $response->addJSON('message', $message);
  423. $response->addJSON($extra_data);
  424. exit;
  425. }
  426. if (! empty($return_to_sql_query)) {
  427. $disp_query = $GLOBALS['sql_query'];
  428. $disp_message = $message;
  429. unset($message);
  430. $GLOBALS['sql_query'] = $return_to_sql_query;
  431. }
  432. $scripts->addFile('vendor/jquery/additional-methods.js');
  433. $scripts->addFile('tbl_change.js');
  434. $active_page = $goto_include;
  435. /**
  436. * If user asked for "and then Insert another new row" we have to remove
  437. * WHERE clause information so that tbl_change.php does not go back
  438. * to the current record
  439. */
  440. if (isset($_POST['after_insert']) && 'new_insert' == $_POST['after_insert']) {
  441. unset($_POST['where_clause']);
  442. }
  443. /**
  444. * Load target page.
  445. */
  446. require '' . Core::securePath($goto_include);
  447. exit;