ImportCsv.php 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818
  1. <?php
  2. /* vim: set expandtab sw=4 ts=4 sts=4: */
  3. /**
  4. * CSV import plugin for phpMyAdmin
  5. *
  6. * @todo add an option for handling NULL values
  7. * @package PhpMyAdmin-Import
  8. * @subpackage CSV
  9. */
  10. declare(strict_types=1);
  11. namespace PhpMyAdmin\Plugins\Import;
  12. use PhpMyAdmin\Import;
  13. use PhpMyAdmin\Message;
  14. use PhpMyAdmin\Properties\Options\Items\BoolPropertyItem;
  15. use PhpMyAdmin\Properties\Options\Items\NumberPropertyItem;
  16. use PhpMyAdmin\Properties\Options\Items\TextPropertyItem;
  17. use PhpMyAdmin\Util;
  18. /**
  19. * Handles the import for the CSV format
  20. *
  21. * @package PhpMyAdmin-Import
  22. * @subpackage CSV
  23. */
  24. class ImportCsv extends AbstractImportCsv
  25. {
  26. /**
  27. * Whether to analyze tables
  28. *
  29. * @var bool
  30. */
  31. private $_analyze;
  32. /**
  33. * Constructor
  34. */
  35. public function __construct()
  36. {
  37. parent::__construct();
  38. $this->setProperties();
  39. }
  40. /**
  41. * Sets the import plugin properties.
  42. * Called in the constructor.
  43. *
  44. * @return void
  45. */
  46. protected function setProperties()
  47. {
  48. $this->_setAnalyze(false);
  49. if ($GLOBALS['plugin_param'] !== 'table') {
  50. $this->_setAnalyze(true);
  51. }
  52. $generalOptions = parent::setProperties();
  53. $this->properties->setText('CSV');
  54. $this->properties->setExtension('csv');
  55. if ($GLOBALS['plugin_param'] !== 'table') {
  56. $leaf = new TextPropertyItem(
  57. "new_tbl_name",
  58. __(
  59. 'Name of the new table (optional):'
  60. )
  61. );
  62. $generalOptions->addProperty($leaf);
  63. if ($GLOBALS['plugin_param'] === 'server') {
  64. $leaf = new TextPropertyItem(
  65. "new_db_name",
  66. __(
  67. 'Name of the new database (optional):'
  68. )
  69. );
  70. $generalOptions->addProperty($leaf);
  71. }
  72. $leaf = new NumberPropertyItem(
  73. "partial_import",
  74. __(
  75. 'Import these many number of rows (optional):'
  76. )
  77. );
  78. $generalOptions->addProperty($leaf);
  79. $leaf = new BoolPropertyItem(
  80. "col_names",
  81. __(
  82. 'The first line of the file contains the table column names'
  83. . ' <i>(if this is unchecked, the first line will become part'
  84. . ' of the data)</i>'
  85. )
  86. );
  87. $generalOptions->addProperty($leaf);
  88. } else {
  89. $leaf = new NumberPropertyItem(
  90. "partial_import",
  91. __(
  92. 'Import these many number of rows (optional):'
  93. )
  94. );
  95. $generalOptions->addProperty($leaf);
  96. $hint = new Message(
  97. __(
  98. 'If the data in each row of the file is not'
  99. . ' in the same order as in the database, list the corresponding'
  100. . ' column names here. Column names must be separated by commas'
  101. . ' and not enclosed in quotations.'
  102. )
  103. );
  104. $leaf = new TextPropertyItem(
  105. "columns",
  106. __('Column names:') . ' ' . Util::showHint($hint)
  107. );
  108. $generalOptions->addProperty($leaf);
  109. }
  110. $leaf = new BoolPropertyItem(
  111. "ignore",
  112. __('Do not abort on INSERT error')
  113. );
  114. $generalOptions->addProperty($leaf);
  115. }
  116. /**
  117. * Handles the whole import logic
  118. *
  119. * @param array $sql_data 2-element array with sql data
  120. *
  121. * @return void
  122. */
  123. public function doImport(array &$sql_data = [])
  124. {
  125. global $db, $table, $csv_terminated, $csv_enclosed, $csv_escaped,
  126. $csv_new_line, $csv_columns, $err_url, $import_file_name;
  127. // $csv_replace and $csv_ignore should have been here,
  128. // but we use directly from $_POST
  129. global $error, $timeout_passed, $finished, $message;
  130. $import_file_name = basename($import_file_name, ".csv");
  131. $import_file_name = mb_strtolower($import_file_name);
  132. $import_file_name = preg_replace("/[^a-zA-Z0-9_]/", "_", $import_file_name);
  133. $replacements = [
  134. '\\n' => "\n",
  135. '\\t' => "\t",
  136. '\\r' => "\r",
  137. ];
  138. $csv_terminated = strtr($csv_terminated, $replacements);
  139. $csv_enclosed = strtr($csv_enclosed, $replacements);
  140. $csv_escaped = strtr($csv_escaped, $replacements);
  141. $csv_new_line = strtr($csv_new_line, $replacements);
  142. $param_error = false;
  143. if (strlen($csv_terminated) === 0) {
  144. $message = Message::error(
  145. __('Invalid parameter for CSV import: %s')
  146. );
  147. $message->addParam(__('Columns terminated with'));
  148. $error = true;
  149. $param_error = true;
  150. // The default dialog of MS Excel when generating a CSV produces a
  151. // semi-colon-separated file with no chance of specifying the
  152. // enclosing character. Thus, users who want to import this file
  153. // tend to remove the enclosing character on the Import dialog.
  154. // I could not find a test case where having no enclosing characters
  155. // confuses this script.
  156. // But the parser won't work correctly with strings so we allow just
  157. // one character.
  158. } elseif (mb_strlen($csv_enclosed) > 1) {
  159. $message = Message::error(
  160. __('Invalid parameter for CSV import: %s')
  161. );
  162. $message->addParam(__('Columns enclosed with'));
  163. $error = true;
  164. $param_error = true;
  165. // I could not find a test case where having no escaping characters
  166. // confuses this script.
  167. // But the parser won't work correctly with strings so we allow just
  168. // one character.
  169. } elseif (mb_strlen($csv_escaped) > 1) {
  170. $message = Message::error(
  171. __('Invalid parameter for CSV import: %s')
  172. );
  173. $message->addParam(__('Columns escaped with'));
  174. $error = true;
  175. $param_error = true;
  176. } elseif (mb_strlen($csv_new_line) != 1
  177. && $csv_new_line != 'auto'
  178. ) {
  179. $message = Message::error(
  180. __('Invalid parameter for CSV import: %s')
  181. );
  182. $message->addParam(__('Lines terminated with'));
  183. $error = true;
  184. $param_error = true;
  185. }
  186. // If there is an error in the parameters entered,
  187. // indicate that immediately.
  188. if ($param_error) {
  189. Util::mysqlDie(
  190. $message->getMessage(),
  191. '',
  192. false,
  193. $err_url
  194. );
  195. }
  196. $buffer = '';
  197. $required_fields = 0;
  198. $sql_template = '';
  199. $fields = [];
  200. if (! $this->_getAnalyze()) {
  201. $sql_template = 'INSERT';
  202. if (isset($_POST['csv_ignore'])) {
  203. $sql_template .= ' IGNORE';
  204. }
  205. $sql_template .= ' INTO ' . Util::backquote($table);
  206. $tmp_fields = $GLOBALS['dbi']->getColumns($db, $table);
  207. if (empty($csv_columns)) {
  208. $fields = $tmp_fields;
  209. } else {
  210. $sql_template .= ' (';
  211. $fields = [];
  212. $tmp = preg_split('/,( ?)/', $csv_columns);
  213. foreach ($tmp as $key => $val) {
  214. if (count($fields) > 0) {
  215. $sql_template .= ', ';
  216. }
  217. /* Trim also `, if user already included backquoted fields */
  218. $val = trim($val, " \t\r\n\0\x0B`");
  219. $found = false;
  220. foreach ($tmp_fields as $field) {
  221. if ($field['Field'] == $val) {
  222. $found = true;
  223. break;
  224. }
  225. }
  226. if (! $found) {
  227. $message = Message::error(
  228. __(
  229. 'Invalid column (%s) specified! Ensure that columns'
  230. . ' names are spelled correctly, separated by commas'
  231. . ', and not enclosed in quotes.'
  232. )
  233. );
  234. $message->addParam($val);
  235. $error = true;
  236. break;
  237. }
  238. if (isset($field)) {
  239. $fields[] = $field;
  240. }
  241. $sql_template .= Util::backquote($val);
  242. }
  243. $sql_template .= ') ';
  244. }
  245. $required_fields = count($fields);
  246. $sql_template .= ' VALUES (';
  247. }
  248. // Defaults for parser
  249. $i = 0;
  250. $len = 0;
  251. $lastlen = null;
  252. $line = 1;
  253. $lasti = -1;
  254. $values = [];
  255. $csv_finish = false;
  256. $max_lines = 0; // defaults to 0 (get all the lines)
  257. // If we get a negative value, probably someone changed min value attribute in DOM or there is an integer overflow, whatever be the case, get all the lines
  258. if (isset($_REQUEST['csv_partial_import']) && $_REQUEST['csv_partial_import'] > 0) {
  259. $max_lines = $_REQUEST['csv_partial_import'];
  260. }
  261. $max_lines_constraint = $max_lines+1;
  262. // if the first row has to be counted as column names, include one more row in the max lines
  263. if (isset($_REQUEST['csv_col_names'])) {
  264. $max_lines_constraint++;
  265. }
  266. $tempRow = [];
  267. $rows = [];
  268. $col_names = [];
  269. $tables = [];
  270. $col_count = 0;
  271. $max_cols = 0;
  272. $csv_terminated_len = mb_strlen($csv_terminated);
  273. while (! ($finished && $i >= $len) && ! $error && ! $timeout_passed) {
  274. $data = $this->import->getNextChunk();
  275. if ($data === false) {
  276. // subtract data we didn't handle yet and stop processing
  277. $GLOBALS['offset'] -= strlen($buffer);
  278. break;
  279. } elseif ($data !== true) {
  280. // Append new data to buffer
  281. $buffer .= $data;
  282. unset($data);
  283. // Force a trailing new line at EOF to prevent parsing problems
  284. if ($finished && $buffer) {
  285. $finalch = mb_substr($buffer, -1);
  286. if ($csv_new_line == 'auto'
  287. && $finalch != "\r"
  288. && $finalch != "\n"
  289. ) {
  290. $buffer .= "\n";
  291. } elseif ($csv_new_line != 'auto'
  292. && $finalch != $csv_new_line
  293. ) {
  294. $buffer .= $csv_new_line;
  295. }
  296. }
  297. // Do not parse string when we're not at the end
  298. // and don't have new line inside
  299. if (($csv_new_line == 'auto'
  300. && mb_strpos($buffer, "\r") === false
  301. && mb_strpos($buffer, "\n") === false)
  302. || ($csv_new_line != 'auto'
  303. && mb_strpos($buffer, $csv_new_line) === false)
  304. ) {
  305. continue;
  306. }
  307. }
  308. // Current length of our buffer
  309. $len = mb_strlen($buffer);
  310. // Currently parsed char
  311. $ch = mb_substr($buffer, $i, 1);
  312. if ($csv_terminated_len > 1 && $ch == $csv_terminated[0]) {
  313. $ch = $this->readCsvTerminatedString(
  314. $buffer,
  315. $ch,
  316. $i,
  317. $csv_terminated_len
  318. );
  319. $i += $csv_terminated_len - 1;
  320. }
  321. while ($i < $len) {
  322. // Deadlock protection
  323. if ($lasti == $i && $lastlen == $len) {
  324. $message = Message::error(
  325. __('Invalid format of CSV input on line %d.')
  326. );
  327. $message->addParam($line);
  328. $error = true;
  329. break;
  330. }
  331. $lasti = $i;
  332. $lastlen = $len;
  333. // This can happen with auto EOL and \r at the end of buffer
  334. if (! $csv_finish) {
  335. // Grab empty field
  336. if ($ch == $csv_terminated) {
  337. if ($i == $len - 1) {
  338. break;
  339. }
  340. $values[] = '';
  341. $i++;
  342. $ch = mb_substr($buffer, $i, 1);
  343. if ($csv_terminated_len > 1 && $ch == $csv_terminated[0]) {
  344. $ch = $this->readCsvTerminatedString(
  345. $buffer,
  346. $ch,
  347. $i,
  348. $csv_terminated_len
  349. );
  350. $i += $csv_terminated_len - 1;
  351. }
  352. continue;
  353. }
  354. // Grab one field
  355. $fallbacki = $i;
  356. if ($ch == $csv_enclosed) {
  357. if ($i == $len - 1) {
  358. break;
  359. }
  360. $need_end = true;
  361. $i++;
  362. $ch = mb_substr($buffer, $i, 1);
  363. if ($csv_terminated_len > 1 && $ch == $csv_terminated[0]) {
  364. $ch = $this->readCsvTerminatedString(
  365. $buffer,
  366. $ch,
  367. $i,
  368. $csv_terminated_len
  369. );
  370. $i += $csv_terminated_len - 1;
  371. }
  372. } else {
  373. $need_end = false;
  374. }
  375. $fail = false;
  376. $value = '';
  377. while (($need_end
  378. && ($ch != $csv_enclosed
  379. || $csv_enclosed == $csv_escaped))
  380. || (! $need_end
  381. && ! ($ch == $csv_terminated
  382. || $ch == $csv_new_line
  383. || ($csv_new_line == 'auto'
  384. && ($ch == "\r" || $ch == "\n"))))
  385. ) {
  386. if ($ch == $csv_escaped) {
  387. if ($i == $len - 1) {
  388. $fail = true;
  389. break;
  390. }
  391. $i++;
  392. $ch = mb_substr($buffer, $i, 1);
  393. if ($csv_terminated_len > 1
  394. && $ch == $csv_terminated[0]
  395. ) {
  396. $ch = $this->readCsvTerminatedString(
  397. $buffer,
  398. $ch,
  399. $i,
  400. $csv_terminated_len
  401. );
  402. $i += $csv_terminated_len - 1;
  403. }
  404. if ($csv_enclosed == $csv_escaped
  405. && ($ch == $csv_terminated
  406. || $ch == $csv_new_line
  407. || ($csv_new_line == 'auto'
  408. && ($ch == "\r" || $ch == "\n")))
  409. ) {
  410. break;
  411. }
  412. }
  413. $value .= $ch;
  414. if ($i == $len - 1) {
  415. if (! $finished) {
  416. $fail = true;
  417. }
  418. break;
  419. }
  420. $i++;
  421. $ch = mb_substr($buffer, $i, 1);
  422. if ($csv_terminated_len > 1 && $ch == $csv_terminated[0]) {
  423. $ch = $this->readCsvTerminatedString(
  424. $buffer,
  425. $ch,
  426. $i,
  427. $csv_terminated_len
  428. );
  429. $i += $csv_terminated_len - 1;
  430. }
  431. }
  432. // unquoted NULL string
  433. if (false === $need_end && $value === 'NULL') {
  434. $value = null;
  435. }
  436. if ($fail) {
  437. $i = $fallbacki;
  438. $ch = mb_substr($buffer, $i, 1);
  439. if ($csv_terminated_len > 1 && $ch == $csv_terminated[0]) {
  440. $i += $csv_terminated_len - 1;
  441. }
  442. break;
  443. }
  444. // Need to strip trailing enclosing char?
  445. if ($need_end && $ch == $csv_enclosed) {
  446. if ($finished && $i == $len - 1) {
  447. $ch = null;
  448. } elseif ($i == $len - 1) {
  449. $i = $fallbacki;
  450. $ch = mb_substr($buffer, $i, 1);
  451. if ($csv_terminated_len > 1
  452. && $ch == $csv_terminated[0]
  453. ) {
  454. $i += $csv_terminated_len - 1;
  455. }
  456. break;
  457. } else {
  458. $i++;
  459. $ch = mb_substr($buffer, $i, 1);
  460. if ($csv_terminated_len > 1
  461. && $ch == $csv_terminated[0]
  462. ) {
  463. $ch = $this->readCsvTerminatedString(
  464. $buffer,
  465. $ch,
  466. $i,
  467. $csv_terminated_len
  468. );
  469. $i += $csv_terminated_len - 1;
  470. }
  471. }
  472. }
  473. // Are we at the end?
  474. if ($ch == $csv_new_line
  475. || ($csv_new_line == 'auto' && ($ch == "\r" || $ch == "\n"))
  476. || ($finished && $i == $len - 1)
  477. ) {
  478. $csv_finish = true;
  479. }
  480. // Go to next char
  481. if ($ch == $csv_terminated) {
  482. if ($i == $len - 1) {
  483. $i = $fallbacki;
  484. $ch = mb_substr($buffer, $i, 1);
  485. if ($csv_terminated_len > 1
  486. && $ch == $csv_terminated[0]
  487. ) {
  488. $i += $csv_terminated_len - 1;
  489. }
  490. break;
  491. }
  492. $i++;
  493. $ch = mb_substr($buffer, $i, 1);
  494. if ($csv_terminated_len > 1
  495. && $ch == $csv_terminated[0]
  496. ) {
  497. $ch = $this->readCsvTerminatedString(
  498. $buffer,
  499. $ch,
  500. $i,
  501. $csv_terminated_len
  502. );
  503. $i += $csv_terminated_len - 1;
  504. }
  505. }
  506. // If everything went okay, store value
  507. $values[] = $value;
  508. }
  509. // End of line
  510. if ($csv_finish
  511. || $ch == $csv_new_line
  512. || ($csv_new_line == 'auto' && ($ch == "\r" || $ch == "\n"))
  513. ) {
  514. if ($csv_new_line == 'auto' && $ch == "\r") { // Handle "\r\n"
  515. if ($i >= ($len - 2) && ! $finished) {
  516. break; // We need more data to decide new line
  517. }
  518. if (mb_substr($buffer, $i + 1, 1) == "\n") {
  519. $i++;
  520. }
  521. }
  522. // We didn't parse value till the end of line, so there was
  523. // empty one
  524. if (! $csv_finish) {
  525. $values[] = '';
  526. }
  527. if ($this->_getAnalyze()) {
  528. foreach ($values as $val) {
  529. $tempRow[] = $val;
  530. ++$col_count;
  531. }
  532. if ($col_count > $max_cols) {
  533. $max_cols = $col_count;
  534. }
  535. $col_count = 0;
  536. $rows[] = $tempRow;
  537. $tempRow = [];
  538. } else {
  539. // Do we have correct count of values?
  540. if (count($values) != $required_fields) {
  541. // Hack for excel
  542. if ($values[count($values) - 1] == ';') {
  543. unset($values[count($values) - 1]);
  544. } else {
  545. $message = Message::error(
  546. __(
  547. 'Invalid column count in CSV input'
  548. . ' on line %d.'
  549. )
  550. );
  551. $message->addParam($line);
  552. $error = true;
  553. break;
  554. }
  555. }
  556. $first = true;
  557. $sql = $sql_template;
  558. foreach ($values as $key => $val) {
  559. if (! $first) {
  560. $sql .= ', ';
  561. }
  562. if ($val === null) {
  563. $sql .= 'NULL';
  564. } else {
  565. $sql .= '\''
  566. . $GLOBALS['dbi']->escapeString($val)
  567. . '\'';
  568. }
  569. $first = false;
  570. }
  571. $sql .= ')';
  572. if (isset($_POST['csv_replace'])) {
  573. $sql .= " ON DUPLICATE KEY UPDATE ";
  574. foreach ($fields as $field) {
  575. $fieldName = Util::backquote(
  576. $field['Field']
  577. );
  578. $sql .= $fieldName . " = VALUES(" . $fieldName
  579. . "), ";
  580. }
  581. $sql = rtrim($sql, ', ');
  582. }
  583. /**
  584. * @todo maybe we could add original line to verbose
  585. * SQL in comment
  586. */
  587. $this->import->runQuery($sql, $sql, $sql_data);
  588. }
  589. $line++;
  590. $csv_finish = false;
  591. $values = [];
  592. $buffer = mb_substr($buffer, $i + 1);
  593. $len = mb_strlen($buffer);
  594. $i = 0;
  595. $lasti = -1;
  596. $ch = mb_substr($buffer, 0, 1);
  597. if ($max_lines > 0 && $line == $max_lines_constraint) {
  598. $finished = 1;
  599. break;
  600. }
  601. }
  602. } // End of parser loop
  603. if ($max_lines > 0 && $line == $max_lines_constraint) {
  604. $finished = 1;
  605. break;
  606. }
  607. } // End of import loop
  608. if ($this->_getAnalyze()) {
  609. /* Fill out all rows */
  610. $num_rows = count($rows);
  611. for ($i = 0; $i < $num_rows; ++$i) {
  612. for ($j = count($rows[$i]); $j < $max_cols; ++$j) {
  613. $rows[$i][] = 'NULL';
  614. }
  615. }
  616. if (isset($_REQUEST['csv_col_names'])) {
  617. $col_names = array_splice($rows, 0, 1);
  618. $col_names = $col_names[0];
  619. // MySQL column names can't end with a space character.
  620. foreach ($col_names as $key => $col_name) {
  621. $col_names[$key] = rtrim($col_name);
  622. }
  623. }
  624. if ((isset($col_names) && count($col_names) != $max_cols)
  625. || ! isset($col_names)
  626. ) {
  627. // Fill out column names
  628. for ($i = 0; $i < $max_cols; ++$i) {
  629. $col_names[] = 'COL ' . ($i + 1);
  630. }
  631. }
  632. // get new table name, if user didn't provide one, set the default name
  633. if (isset($_REQUEST['csv_new_tbl_name'])
  634. && strlen($_REQUEST['csv_new_tbl_name']) > 0
  635. ) {
  636. $tbl_name = $_REQUEST['csv_new_tbl_name'];
  637. } elseif (mb_strlen((string) $db)) {
  638. $result = $GLOBALS['dbi']->fetchResult('SHOW TABLES');
  639. // logic to get table name from filename
  640. // if no table then use filename as table name
  641. if (count($result) === 0) {
  642. $tbl_name = $import_file_name;
  643. } else {
  644. // check to see if {filename} as table exist
  645. $name_array = preg_grep("/{$import_file_name}/isU", $result);
  646. // if no use filename as table name
  647. if (count($name_array) === 0) {
  648. $tbl_name = $import_file_name;
  649. } else {
  650. // check if {filename}_ as table exist
  651. $name_array = preg_grep("/{$import_file_name}_/isU", $result);
  652. $tbl_name = $import_file_name . "_" . (count($name_array) + 1);
  653. }
  654. }
  655. } else {
  656. $tbl_name = $import_file_name;
  657. }
  658. $tables[] = [
  659. $tbl_name,
  660. $col_names,
  661. $rows,
  662. ];
  663. /* Obtain the best-fit MySQL types for each column */
  664. $analyses = [];
  665. $analyses[] = $this->import->analyzeTable($tables[0]);
  666. /**
  667. * string $db_name (no backquotes)
  668. *
  669. * array $table = array(table_name, array() column_names, array()() rows)
  670. * array $tables = array of "$table"s
  671. *
  672. * array $analysis = array(array() column_types, array() column_sizes)
  673. * array $analyses = array of "$analysis"s
  674. *
  675. * array $create = array of SQL strings
  676. *
  677. * array $options = an associative array of options
  678. */
  679. /* Set database name to the currently selected one, if applicable,
  680. * Otherwise, check if user provided the database name in the request,
  681. * if not, set the default name
  682. */
  683. if (isset($_REQUEST['csv_new_db_name'])
  684. && strlen($_REQUEST['csv_new_db_name']) > 0
  685. ) {
  686. $newDb = $_REQUEST['csv_new_db_name'];
  687. } else {
  688. $result = $GLOBALS['dbi']->fetchResult('SHOW DATABASES');
  689. if (! is_array($result)) {
  690. $result = [];
  691. }
  692. $newDb = 'CSV_DB ' . (count($result) + 1);
  693. }
  694. list($db_name, $options) = $this->getDbnameAndOptions($db, $newDb);
  695. /* Non-applicable parameters */
  696. $create = null;
  697. /* Created and execute necessary SQL statements from data */
  698. $this->import->buildSql($db_name, $tables, $analyses, $create, $options, $sql_data);
  699. unset($tables);
  700. unset($analyses);
  701. }
  702. // Commit any possible data in buffers
  703. $this->import->runQuery('', '', $sql_data);
  704. if (count($values) != 0 && ! $error) {
  705. $message = Message::error(
  706. __('Invalid format of CSV input on line %d.')
  707. );
  708. $message->addParam($line);
  709. $error = true;
  710. }
  711. }
  712. /**
  713. * Read the expected column_separated_with String of length
  714. * $csv_terminated_len from the $buffer
  715. * into variable $ch and return the read string $ch
  716. *
  717. * @param string $buffer The original string buffer read from
  718. * csv file
  719. * @param string $ch Partially read "column Separated with"
  720. * string, also used to return after
  721. * reading length equal $csv_terminated_len
  722. * @param int $i Current read counter of buffer string
  723. * @param int $csv_terminated_len The length of "column separated with"
  724. * String
  725. *
  726. * @return string
  727. */
  728. public function readCsvTerminatedString($buffer, $ch, $i, $csv_terminated_len)
  729. {
  730. for ($j = 0; $j < $csv_terminated_len - 1; $j++) {
  731. $i++;
  732. $ch .= mb_substr($buffer, $i, 1);
  733. }
  734. return $ch;
  735. }
  736. /* ~~~~~~~~~~~~~~~~~~~~ Getters and Setters ~~~~~~~~~~~~~~~~~~~~ */
  737. /**
  738. * Returns true if the table should be analyzed, false otherwise
  739. *
  740. * @return bool
  741. */
  742. private function _getAnalyze()
  743. {
  744. return $this->_analyze;
  745. }
  746. /**
  747. * Sets to true if the table should be analyzed, false otherwise
  748. *
  749. * @param bool $analyze status
  750. *
  751. * @return void
  752. */
  753. private function _setAnalyze($analyze)
  754. {
  755. $this->_analyze = $analyze;
  756. }
  757. }