import.lib.php 41 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248
  1. <?php
  2. /* vim: set expandtab sw=4 ts=4 sts=4: */
  3. /**
  4. * Library that provides common import functions that are used by import plugins
  5. *
  6. * @package PhpMyAdmin-Import
  7. */
  8. if (! defined('PHPMYADMIN')) {
  9. exit;
  10. }
  11. /**
  12. * We need to know something about user
  13. */
  14. require_once './libraries/check_user_privileges.lib.php';
  15. /**
  16. * We do this check, DROP DATABASE does not need to be confirmed elsewhere
  17. */
  18. define('PMA_CHK_DROP', 1);
  19. /**
  20. * Checks whether timeout is getting close
  21. *
  22. * @return boolean true if timeout is close
  23. * @access public
  24. */
  25. function PMA_checkTimeout()
  26. {
  27. global $timestamp, $maximum_time, $timeout_passed;
  28. if ($maximum_time == 0) {
  29. return false;
  30. } elseif ($timeout_passed) {
  31. return true;
  32. /* 5 in next row might be too much */
  33. } elseif ((time() - $timestamp) > ($maximum_time - 5)) {
  34. $timeout_passed = true;
  35. return true;
  36. } else {
  37. return false;
  38. }
  39. }
  40. /**
  41. * Detects what compression filse uses
  42. *
  43. * @param string $filepath filename to check
  44. *
  45. * @return string MIME type of compression, none for none
  46. * @access public
  47. */
  48. function PMA_detectCompression($filepath)
  49. {
  50. $file = @fopen($filepath, 'rb');
  51. if (! $file) {
  52. return false;
  53. }
  54. $test = fread($file, 4);
  55. $len = strlen($test);
  56. fclose($file);
  57. if ($len >= 2 && $test[0] == chr(31) && $test[1] == chr(139)) {
  58. return 'application/gzip';
  59. }
  60. if ($len >= 3 && substr($test, 0, 3) == 'BZh') {
  61. return 'application/bzip2';
  62. }
  63. if ($len >= 4 && $test == "PK\003\004") {
  64. return 'application/zip';
  65. }
  66. return 'none';
  67. }
  68. /**
  69. * Runs query inside import buffer. This is needed to allow displaying
  70. * of last SELECT, SHOW or HANDLER results and similar nice stuff.
  71. *
  72. * @param string $sql query to run
  73. * @param string $full query to display, this might be commented
  74. * @param bool $controluser whether to use control user for queries
  75. * @param array &$sql_data
  76. *
  77. * @return void
  78. * @access public
  79. */
  80. function PMA_importRunQuery($sql = '', $full = '', $controluser = false,
  81. &$sql_data = array()
  82. ) {
  83. global $import_run_buffer, $go_sql, $complete_query, $display_query,
  84. $sql_query, $my_die, $error, $reload,
  85. $last_query_with_results, $result, $msg,
  86. $skip_queries, $executed_queries, $max_sql_len, $read_multiply,
  87. $cfg, $sql_query_disabled, $db, $run_query, $is_superuser;
  88. $read_multiply = 1;
  89. if (isset($import_run_buffer)) {
  90. // Should we skip something?
  91. if ($skip_queries > 0) {
  92. $skip_queries--;
  93. } else {
  94. if (! empty($import_run_buffer['sql'])
  95. && trim($import_run_buffer['sql']) != ''
  96. ) {
  97. // USE query changes the database, son need to track
  98. // while running multiple queries
  99. $is_use_query
  100. = (stripos($import_run_buffer['sql'], "use ") !== false)
  101. ? true
  102. : false;
  103. $max_sql_len = max($max_sql_len, strlen($import_run_buffer['sql']));
  104. if (! $sql_query_disabled) {
  105. $sql_query .= $import_run_buffer['full'];
  106. }
  107. if (! $cfg['AllowUserDropDatabase']
  108. && ! $is_superuser
  109. && preg_match('@^[[:space:]]*DROP[[:space:]]+(IF EXISTS[[:space:]]+)?DATABASE @i', $import_run_buffer['sql'])
  110. ) {
  111. $GLOBALS['message'] = PMA_Message::error(__('"DROP DATABASE" statements are disabled.'));
  112. $error = true;
  113. } else {
  114. $executed_queries++;
  115. if ($run_query
  116. && $GLOBALS['finished']
  117. && empty($sql)
  118. && ! $error
  119. && ((! empty($import_run_buffer['sql'])
  120. && preg_match('/^[\s]*(SELECT|SHOW|HANDLER)/i', $import_run_buffer['sql']))
  121. || ($executed_queries == 1))
  122. ) {
  123. $go_sql = true;
  124. if (! $sql_query_disabled) {
  125. $complete_query = $sql_query;
  126. $display_query = $sql_query;
  127. } else {
  128. $complete_query = '';
  129. $display_query = '';
  130. }
  131. $sql_query = $import_run_buffer['sql'];
  132. $sql_data['valid_sql'][] = $import_run_buffer['sql'];
  133. $sql_data['valid_queries']++;
  134. // If a 'USE <db>' SQL-clause was found,
  135. // set our current $db to the new one
  136. list($db, $reload) = PMA_lookForUse(
  137. $import_run_buffer['sql'],
  138. $db,
  139. $reload
  140. );
  141. } elseif ($run_query) {
  142. if ($controluser) {
  143. $result = PMA_queryAsControlUser(
  144. $import_run_buffer['sql']
  145. );
  146. } else {
  147. $result = PMA_DBI_try_query($import_run_buffer['sql']);
  148. }
  149. $msg = '# ';
  150. if ($result === false) { // execution failed
  151. if (! isset($my_die)) {
  152. $my_die = array();
  153. }
  154. $my_die[] = array(
  155. 'sql' => $import_run_buffer['full'],
  156. 'error' => PMA_DBI_getError()
  157. );
  158. $msg .= __('Error');
  159. if (! $cfg['IgnoreMultiSubmitErrors']) {
  160. $error = true;
  161. return;
  162. }
  163. } else {
  164. $a_num_rows = (int)@PMA_DBI_num_rows($result);
  165. $a_aff_rows = (int)@PMA_DBI_affected_rows();
  166. if ($a_num_rows > 0) {
  167. $msg .= __('Rows'). ': ' . $a_num_rows;
  168. $last_query_with_results = $import_run_buffer['sql'];
  169. } elseif ($a_aff_rows > 0) {
  170. $message = PMA_Message::getMessageForAffectedRows($a_aff_rows);
  171. $msg .= $message->getMessage();
  172. } else {
  173. $msg .= __('MySQL returned an empty result set (i.e. zero rows).');
  174. }
  175. if (($a_num_rows > 0) || $is_use_query) {
  176. $sql_data['valid_sql'][] = $import_run_buffer['sql'];
  177. $sql_data['valid_queries']++;
  178. }
  179. }
  180. if (! $sql_query_disabled) {
  181. $sql_query .= $msg . "\n";
  182. }
  183. // If a 'USE <db>' SQL-clause was found and the query
  184. // succeeded, set our current $db to the new one
  185. if ($result != false) {
  186. list($db, $reload) = PMA_lookForUse(
  187. $import_run_buffer['sql'],
  188. $db,
  189. $reload
  190. );
  191. }
  192. if ($result != false
  193. && preg_match('@^[\s]*(DROP|CREATE)[\s]+(IF EXISTS[[:space:]]+)?(TABLE|DATABASE)[[:space:]]+(.+)@im', $import_run_buffer['sql'])
  194. ) {
  195. $reload = true;
  196. }
  197. } // end run query
  198. } // end if not DROP DATABASE
  199. // end non empty query
  200. } elseif (! empty($import_run_buffer['full'])) {
  201. if ($go_sql) {
  202. $complete_query .= $import_run_buffer['full'];
  203. $display_query .= $import_run_buffer['full'];
  204. } else {
  205. if (! $sql_query_disabled) {
  206. $sql_query .= $import_run_buffer['full'];
  207. }
  208. }
  209. }
  210. // check length of query unless we decided to pass it to sql.php
  211. // (if $run_query is false, we are just displaying so show
  212. // the complete query in the textarea)
  213. if (! $go_sql && $run_query) {
  214. if (! empty($sql_query)) {
  215. if (strlen($sql_query) > 50000
  216. || $executed_queries > 50
  217. || $max_sql_len > 1000
  218. ) {
  219. $sql_query = '';
  220. $sql_query_disabled = true;
  221. }
  222. }
  223. }
  224. } // end do query (no skip)
  225. } // end buffer exists
  226. // Do we have something to push into buffer?
  227. if (! empty($sql) || ! empty($full)) {
  228. $import_run_buffer = array('sql' => $sql, 'full' => $full);
  229. } else {
  230. unset($GLOBALS['import_run_buffer']);
  231. }
  232. }
  233. /**
  234. * Looks for the presence of USE to possibly change current db
  235. *
  236. * @param string $buffer buffer to examine
  237. * @param string $db current db
  238. * @param bool $reload reload
  239. *
  240. * @return array (current or new db, whether to reload)
  241. * @access public
  242. */
  243. function PMA_lookForUse($buffer, $db, $reload)
  244. {
  245. if (preg_match('@^[\s]*USE[[:space:]]+([\S]+)@i', $buffer, $match)) {
  246. $db = trim($match[1]);
  247. $db = trim($db, ';'); // for example, USE abc;
  248. // $db must not contain the escape characters generated by backquote()
  249. // ( used in PMA_buildSQL() as: backquote($db_name), and then called
  250. // in PMA_importRunQuery() which in turn calls PMA_lookForUse() )
  251. $db = PMA_Util::unQuote($db);
  252. $reload = true;
  253. }
  254. return(array($db, $reload));
  255. }
  256. /**
  257. * Returns next part of imported file/buffer
  258. *
  259. * @param int $size size of buffer to read
  260. * (this is maximal size function will return)
  261. *
  262. * @return string part of file/buffer
  263. * @access public
  264. */
  265. function PMA_importGetNextChunk($size = 32768)
  266. {
  267. global $compression, $import_handle, $charset_conversion, $charset_of_file,
  268. $read_multiply;
  269. // Add some progression while reading large amount of data
  270. if ($read_multiply <= 8) {
  271. $size *= $read_multiply;
  272. } else {
  273. $size *= 8;
  274. }
  275. $read_multiply++;
  276. // We can not read too much
  277. if ($size > $GLOBALS['read_limit']) {
  278. $size = $GLOBALS['read_limit'];
  279. }
  280. if (PMA_checkTimeout()) {
  281. return false;
  282. }
  283. if ($GLOBALS['finished']) {
  284. return true;
  285. }
  286. if ($GLOBALS['import_file'] == 'none') {
  287. // Well this is not yet supported and tested,
  288. // but should return content of textarea
  289. if (strlen($GLOBALS['import_text']) < $size) {
  290. $GLOBALS['finished'] = true;
  291. return $GLOBALS['import_text'];
  292. } else {
  293. $r = substr($GLOBALS['import_text'], 0, $size);
  294. $GLOBALS['offset'] += $size;
  295. $GLOBALS['import_text'] = substr($GLOBALS['import_text'], $size);
  296. return $r;
  297. }
  298. }
  299. switch ($compression) {
  300. case 'application/bzip2':
  301. $result = bzread($import_handle, $size);
  302. $GLOBALS['finished'] = feof($import_handle);
  303. break;
  304. case 'application/gzip':
  305. $result = gzread($import_handle, $size);
  306. $GLOBALS['finished'] = feof($import_handle);
  307. break;
  308. case 'application/zip':
  309. $result = substr($GLOBALS['import_text'], 0, $size);
  310. $GLOBALS['import_text'] = substr($GLOBALS['import_text'], $size);
  311. $GLOBALS['finished'] = empty($GLOBALS['import_text']);
  312. break;
  313. case 'none':
  314. $result = fread($import_handle, $size);
  315. $GLOBALS['finished'] = feof($import_handle);
  316. break;
  317. }
  318. $GLOBALS['offset'] += $size;
  319. if ($charset_conversion) {
  320. return PMA_convert_string($charset_of_file, 'utf-8', $result);
  321. } else {
  322. /**
  323. * Skip possible byte order marks (I do not think we need more
  324. * charsets, but feel free to add more, you can use wikipedia for
  325. * reference: <http://en.wikipedia.org/wiki/Byte_Order_Mark>)
  326. *
  327. * @todo BOM could be used for charset autodetection
  328. */
  329. if ($GLOBALS['offset'] == $size) {
  330. // UTF-8
  331. if (strncmp($result, "\xEF\xBB\xBF", 3) == 0) {
  332. $result = substr($result, 3);
  333. // UTF-16 BE, LE
  334. } elseif (strncmp($result, "\xFE\xFF", 2) == 0
  335. || strncmp($result, "\xFF\xFE", 2) == 0
  336. ) {
  337. $result = substr($result, 2);
  338. }
  339. }
  340. return $result;
  341. }
  342. }
  343. /**
  344. * Returns the "Excel" column name (i.e. 1 = "A", 26 = "Z", 27 = "AA", etc.)
  345. *
  346. * This functions uses recursion to build the Excel column name.
  347. *
  348. * The column number (1-26) is converted to the responding
  349. * ASCII character (A-Z) and returned.
  350. *
  351. * If the column number is bigger than 26 (= num of letters in alfabet),
  352. * an extra character needs to be added. To find this extra character,
  353. * the number is divided by 26 and this value is passed to another instance
  354. * of the same function (hence recursion). In that new instance the number is
  355. * evaluated again, and if it is still bigger than 26, it is divided again
  356. * and passed to another instance of the same function. This continues until
  357. * the number is smaller than 26. Then the last called function returns
  358. * the corresponding ASCII character to the function that called it.
  359. * Each time a called function ends an extra character is added to the column name.
  360. * When the first function is reached, the last character is addded and the complete
  361. * column name is returned.
  362. *
  363. * @param int $num the column number
  364. *
  365. * @return string The column's "Excel" name
  366. * @access public
  367. */
  368. function PMA_getColumnAlphaName($num)
  369. {
  370. $A = 65; // ASCII value for capital "A"
  371. $col_name = "";
  372. if ($num > 26) {
  373. $div = (int)($num / 26);
  374. $remain = (int)($num % 26);
  375. // subtract 1 of divided value in case the modulus is 0,
  376. // this is necessary because A-Z has no 'zero'
  377. if ($remain == 0) {
  378. $div--;
  379. }
  380. // recursive function call
  381. $col_name = PMA_getColumnAlphaName($div);
  382. // use modulus as new column number
  383. $num = $remain;
  384. }
  385. if ($num == 0) {
  386. // use 'Z' if column number is 0,
  387. // this is necessary because A-Z has no 'zero'
  388. $col_name .= chr(($A + 26) - 1);
  389. } else {
  390. // convert column number to ASCII character
  391. $col_name .= chr(($A + $num) - 1);
  392. }
  393. return $col_name;
  394. }
  395. /**
  396. * Returns the column number based on the Excel name.
  397. * So "A" = 1, "Z" = 26, "AA" = 27, etc.
  398. *
  399. * Basicly this is a base26 (A-Z) to base10 (0-9) conversion.
  400. * It iterates through all characters in the column name and
  401. * calculates the corresponding value, based on character value
  402. * (A = 1, ..., Z = 26) and position in the string.
  403. *
  404. * @param string $name column name(i.e. "A", or "BC", etc.)
  405. *
  406. * @return int The column number
  407. * @access public
  408. */
  409. function PMA_getColumnNumberFromName($name)
  410. {
  411. if (! empty($name)) {
  412. $name = strtoupper($name);
  413. $num_chars = strlen($name);
  414. $column_number = 0;
  415. for ($i = 0; $i < $num_chars; ++$i) {
  416. // read string from back to front
  417. $char_pos = ($num_chars - 1) - $i;
  418. // convert capital character to ASCII value
  419. // and subtract 64 to get corresponding decimal value
  420. // ASCII value of "A" is 65, "B" is 66, etc.
  421. // Decimal equivalent of "A" is 1, "B" is 2, etc.
  422. $number = (ord($name[$char_pos]) - 64);
  423. // base26 to base10 conversion : multiply each number
  424. // with corresponding value of the position, in this case
  425. // $i=0 : 1; $i=1 : 26; $i=2 : 676; ...
  426. $column_number += $number * PMA_Util::pow(26, $i);
  427. }
  428. return $column_number;
  429. } else {
  430. return 0;
  431. }
  432. }
  433. /**
  434. * Constants definitions
  435. */
  436. /* MySQL type defs */
  437. define("NONE", 0);
  438. define("VARCHAR", 1);
  439. define("INT", 2);
  440. define("DECIMAL", 3);
  441. define("BIGINT", 4);
  442. define("GEOMETRY", 5);
  443. /* Decimal size defs */
  444. define("M", 0);
  445. define("D", 1);
  446. define("FULL", 2);
  447. /* Table array defs */
  448. define("TBL_NAME", 0);
  449. define("COL_NAMES", 1);
  450. define("ROWS", 2);
  451. /* Analysis array defs */
  452. define("TYPES", 0);
  453. define("SIZES", 1);
  454. define("FORMATTEDSQL", 2);
  455. /**
  456. * Obtains the precision (total # of digits) from a size of type decimal
  457. *
  458. * @param string $last_cumulative_size
  459. *
  460. * @return int Precision of the given decimal size notation
  461. * @access public
  462. */
  463. function PMA_getM($last_cumulative_size)
  464. {
  465. return (int)substr($last_cumulative_size, 0, strpos($last_cumulative_size, ","));
  466. }
  467. /**
  468. * Obtains the scale (# of digits to the right of the decimal point)
  469. * from a size of type decimal
  470. *
  471. * @param string $last_cumulative_size
  472. *
  473. * @return int Scale of the given decimal size notation
  474. * @access public
  475. */
  476. function PMA_getD($last_cumulative_size)
  477. {
  478. return (int) substr(
  479. $last_cumulative_size,
  480. (strpos($last_cumulative_size, ",") + 1),
  481. (strlen($last_cumulative_size) - strpos($last_cumulative_size, ","))
  482. );
  483. }
  484. /**
  485. * Obtains the decimal size of a given cell
  486. *
  487. * @param string $cell cell content
  488. *
  489. * @return array Contains the precision, scale, and full size
  490. * representation of the given decimal cell
  491. * @access public
  492. */
  493. function PMA_getDecimalSize($cell)
  494. {
  495. $curr_size = strlen((string)$cell);
  496. $decPos = strpos($cell, ".");
  497. $decPrecision = ($curr_size - 1) - $decPos;
  498. $m = $curr_size - 1;
  499. $d = $decPrecision;
  500. return array($m, $d, ($m . "," . $d));
  501. }
  502. /**
  503. * Obtains the size of the given cell
  504. *
  505. * @param string $last_cumulative_size Last cumulative column size
  506. * @param int $last_cumulative_type Last cumulative column type
  507. * (NONE or VARCHAR or DECIMAL or INT or BIGINT)
  508. * @param int $curr_type Type of the current cell
  509. * (NONE or VARCHAR or DECIMAL or INT or BIGINT)
  510. * @param string $cell The current cell
  511. *
  512. * @return string Size of the given cell in the type-appropriate format
  513. * @access public
  514. *
  515. * @todo Handle the error cases more elegantly
  516. */
  517. function PMA_detectSize($last_cumulative_size, $last_cumulative_type,
  518. $curr_type, $cell
  519. ) {
  520. $curr_size = strlen((string)$cell);
  521. /**
  522. * If the cell is NULL, don't treat it as a varchar
  523. */
  524. if (! strcmp('NULL', $cell)) {
  525. return $last_cumulative_size;
  526. } elseif ($curr_type == VARCHAR) {
  527. /**
  528. * What to do if the current cell is of type VARCHAR
  529. */
  530. /**
  531. * The last cumulative type was VARCHAR
  532. */
  533. if ($last_cumulative_type == VARCHAR) {
  534. if ($curr_size >= $last_cumulative_size) {
  535. return $curr_size;
  536. } else {
  537. return $last_cumulative_size;
  538. }
  539. } elseif ($last_cumulative_type == DECIMAL) {
  540. /**
  541. * The last cumulative type was DECIMAL
  542. */
  543. $oldM = PMA_getM($last_cumulative_size);
  544. if ($curr_size >= $oldM) {
  545. return $curr_size;
  546. } else {
  547. return $oldM;
  548. }
  549. } elseif ($last_cumulative_type == BIGINT || $last_cumulative_type == INT) {
  550. /**
  551. * The last cumulative type was BIGINT or INT
  552. */
  553. if ($curr_size >= $last_cumulative_size) {
  554. return $curr_size;
  555. } else {
  556. return $last_cumulative_size;
  557. }
  558. } elseif (! isset($last_cumulative_type) || $last_cumulative_type == NONE) {
  559. /**
  560. * This is the first row to be analyzed
  561. */
  562. return $curr_size;
  563. } else {
  564. /**
  565. * An error has DEFINITELY occurred
  566. */
  567. /**
  568. * TODO: Handle this MUCH more elegantly
  569. */
  570. return -1;
  571. }
  572. } elseif ($curr_type == DECIMAL) {
  573. /**
  574. * What to do if the current cell is of type DECIMAL
  575. */
  576. /**
  577. * The last cumulative type was VARCHAR
  578. */
  579. if ($last_cumulative_type == VARCHAR) {
  580. /* Convert $last_cumulative_size from varchar to decimal format */
  581. $size = PMA_getDecimalSize($cell);
  582. if ($size[M] >= $last_cumulative_size) {
  583. return $size[M];
  584. } else {
  585. return $last_cumulative_size;
  586. }
  587. } elseif ($last_cumulative_type == DECIMAL) {
  588. /**
  589. * The last cumulative type was DECIMAL
  590. */
  591. $size = PMA_getDecimalSize($cell);
  592. $oldM = PMA_getM($last_cumulative_size);
  593. $oldD = PMA_getD($last_cumulative_size);
  594. /* New val if M or D is greater than current largest */
  595. if ($size[M] > $oldM || $size[D] > $oldD) {
  596. /* Take the largest of both types */
  597. return (string) ((($size[M] > $oldM) ? $size[M] : $oldM)
  598. . "," . (($size[D] > $oldD) ? $size[D] : $oldD));
  599. } else {
  600. return $last_cumulative_size;
  601. }
  602. } elseif ($last_cumulative_type == BIGINT || $last_cumulative_type == INT) {
  603. /**
  604. * The last cumulative type was BIGINT or INT
  605. */
  606. /* Convert $last_cumulative_size from int to decimal format */
  607. $size = PMA_getDecimalSize($cell);
  608. if ($size[M] >= $last_cumulative_size) {
  609. return $size[FULL];
  610. } else {
  611. return ($last_cumulative_size.",".$size[D]);
  612. }
  613. } elseif (! isset($last_cumulative_type) || $last_cumulative_type == NONE) {
  614. /**
  615. * This is the first row to be analyzed
  616. */
  617. /* First row of the column */
  618. $size = PMA_getDecimalSize($cell);
  619. return $size[FULL];
  620. } else {
  621. /**
  622. * An error has DEFINITELY occurred
  623. */
  624. /**
  625. * TODO: Handle this MUCH more elegantly
  626. */
  627. return -1;
  628. }
  629. } elseif ($curr_type == BIGINT || $curr_type == INT) {
  630. /**
  631. * What to do if the current cell is of type BIGINT or INT
  632. */
  633. /**
  634. * The last cumulative type was VARCHAR
  635. */
  636. if ($last_cumulative_type == VARCHAR) {
  637. if ($curr_size >= $last_cumulative_size) {
  638. return $curr_size;
  639. } else {
  640. return $last_cumulative_size;
  641. }
  642. } elseif ($last_cumulative_type == DECIMAL) {
  643. /**
  644. * The last cumulative type was DECIMAL
  645. */
  646. $oldM = PMA_getM($last_cumulative_size);
  647. $oldD = PMA_getD($last_cumulative_size);
  648. $oldInt = $oldM - $oldD;
  649. $newInt = strlen((string)$cell);
  650. /* See which has the larger integer length */
  651. if ($oldInt >= $newInt) {
  652. /* Use old decimal size */
  653. return $last_cumulative_size;
  654. } else {
  655. /* Use $newInt + $oldD as new M */
  656. return (($newInt + $oldD) . "," . $oldD);
  657. }
  658. } elseif ($last_cumulative_type == BIGINT || $last_cumulative_type == INT) {
  659. /**
  660. * The last cumulative type was BIGINT or INT
  661. */
  662. if ($curr_size >= $last_cumulative_size) {
  663. return $curr_size;
  664. } else {
  665. return $last_cumulative_size;
  666. }
  667. } elseif (! isset($last_cumulative_type) || $last_cumulative_type == NONE) {
  668. /**
  669. * This is the first row to be analyzed
  670. */
  671. return $curr_size;
  672. } else {
  673. /**
  674. * An error has DEFINITELY occurred
  675. */
  676. /**
  677. * TODO: Handle this MUCH more elegantly
  678. */
  679. return -1;
  680. }
  681. } else {
  682. /**
  683. * An error has DEFINITELY occurred
  684. */
  685. /**
  686. * TODO: Handle this MUCH more elegantly
  687. */
  688. return -1;
  689. }
  690. }
  691. /**
  692. * Determines what MySQL type a cell is
  693. *
  694. * @param int $last_cumulative_type Last cumulative column type
  695. * (VARCHAR or INT or BIGINT or DECIMAL or NONE)
  696. * @param string $cell String representation of the cell for which
  697. * a best-fit type is to be determined
  698. *
  699. * @return int The MySQL type representation
  700. * (VARCHAR or INT or BIGINT or DECIMAL or NONE)
  701. * @access public
  702. */
  703. function PMA_detectType($last_cumulative_type, $cell)
  704. {
  705. /**
  706. * If numeric, determine if decimal, int or bigint
  707. * Else, we call it varchar for simplicity
  708. */
  709. if (! strcmp('NULL', $cell)) {
  710. if ($last_cumulative_type === null || $last_cumulative_type == NONE) {
  711. return NONE;
  712. } else {
  713. return $last_cumulative_type;
  714. }
  715. } elseif (is_numeric($cell)) {
  716. if ($cell == (string)(float)$cell
  717. && strpos($cell, ".") !== false
  718. && substr_count($cell, ".") == 1
  719. ) {
  720. return DECIMAL;
  721. } else {
  722. if (abs($cell) > 2147483647) {
  723. return BIGINT;
  724. } else {
  725. return INT;
  726. }
  727. }
  728. } else {
  729. return VARCHAR;
  730. }
  731. }
  732. /**
  733. * Determines if the column types are int, decimal, or string
  734. *
  735. * @param array &$table array(string $table_name, array $col_names, array $rows)
  736. *
  737. * @return array array(array $types, array $sizes)
  738. * @access public
  739. *
  740. * @link https://wiki.phpmyadmin.net/pma/Import
  741. *
  742. * @todo Handle the error case more elegantly
  743. */
  744. function PMA_analyzeTable(&$table)
  745. {
  746. /* Get number of rows in table */
  747. $numRows = count($table[ROWS]);
  748. /* Get number of columns */
  749. $numCols = count($table[COL_NAMES]);
  750. /* Current type for each column */
  751. $types = array();
  752. $sizes = array();
  753. /* Initialize $sizes to all 0's */
  754. for ($i = 0; $i < $numCols; ++$i) {
  755. $sizes[$i] = 0;
  756. }
  757. /* Initialize $types to NONE */
  758. for ($i = 0; $i < $numCols; ++$i) {
  759. $types[$i] = NONE;
  760. }
  761. /* Temp vars */
  762. $curr_type = NONE;
  763. /* If the passed array is not of the correct form, do not process it */
  764. if (is_array($table)
  765. && ! is_array($table[TBL_NAME])
  766. && is_array($table[COL_NAMES])
  767. && is_array($table[ROWS])
  768. ) {
  769. /* Analyze each column */
  770. for ($i = 0; $i < $numCols; ++$i) {
  771. /* Analyze the column in each row */
  772. for ($j = 0; $j < $numRows; ++$j) {
  773. /* Determine type of the current cell */
  774. $curr_type = PMA_detectType($types[$i], $table[ROWS][$j][$i]);
  775. /* Determine size of the current cell */
  776. $sizes[$i] = PMA_detectSize(
  777. $sizes[$i],
  778. $types[$i],
  779. $curr_type,
  780. $table[ROWS][$j][$i]
  781. );
  782. /**
  783. * If a type for this column has already been declared,
  784. * only alter it if it was a number and a varchar was found
  785. */
  786. if ($curr_type != NONE) {
  787. if ($curr_type == VARCHAR) {
  788. $types[$i] = VARCHAR;
  789. } else if ($curr_type == DECIMAL) {
  790. if ($types[$i] != VARCHAR) {
  791. $types[$i] = DECIMAL;
  792. }
  793. } else if ($curr_type == BIGINT) {
  794. if ($types[$i] != VARCHAR && $types[$i] != DECIMAL) {
  795. $types[$i] = BIGINT;
  796. }
  797. } else if ($curr_type == INT) {
  798. if ($types[$i] != VARCHAR
  799. && $types[$i] != DECIMAL
  800. && $types[$i] != BIGINT
  801. ) {
  802. $types[$i] = INT;
  803. }
  804. }
  805. }
  806. }
  807. }
  808. /* Check to ensure that all types are valid */
  809. $len = count($types);
  810. for ($n = 0; $n < $len; ++$n) {
  811. if (! strcmp(NONE, $types[$n])) {
  812. $types[$n] = VARCHAR;
  813. $sizes[$n] = '10';
  814. }
  815. }
  816. return array($types, $sizes);
  817. } else {
  818. /**
  819. * TODO: Handle this better
  820. */
  821. return false;
  822. }
  823. }
  824. /* Needed to quell the beast that is PMA_Message */
  825. $import_notice = null;
  826. /**
  827. * Builds and executes SQL statements to create the database and tables
  828. * as necessary, as well as insert all the data.
  829. *
  830. * @param string $db_name Name of the database
  831. * @param array &$tables Array of tables for the specified database
  832. * @param array &$analyses Analyses of the tables
  833. * @param array &$additional_sql Additional SQL statements to be executed
  834. * @param array $options Associative array of options
  835. *
  836. * @return void
  837. * @access public
  838. *
  839. * @link https://wiki.phpmyadmin.net/pma/Import
  840. */
  841. function PMA_buildSQL($db_name, &$tables, &$analyses = null,
  842. &$additional_sql = null, $options = null
  843. ) {
  844. /* Take care of the options */
  845. if (isset($options['db_collation'])&& ! is_null($options['db_collation'])) {
  846. $collation = $options['db_collation'];
  847. } else {
  848. $collation = "utf8_general_ci";
  849. }
  850. if (isset($options['db_charset']) && ! is_null($options['db_charset'])) {
  851. $charset = $options['db_charset'];
  852. } else {
  853. $charset = "utf8";
  854. }
  855. if (isset($options['create_db'])) {
  856. $create_db = $options['create_db'];
  857. } else {
  858. $create_db = true;
  859. }
  860. /* Create SQL code to handle the database */
  861. $sql = array();
  862. if ($create_db) {
  863. if (PMA_DRIZZLE) {
  864. $sql[] = "CREATE DATABASE IF NOT EXISTS " . PMA_Util::backquote($db_name)
  865. . " COLLATE " . $collation;
  866. } else {
  867. $sql[] = "CREATE DATABASE IF NOT EXISTS " . PMA_Util::backquote($db_name)
  868. . " DEFAULT CHARACTER SET " . $charset . " COLLATE " . $collation;
  869. }
  870. }
  871. /**
  872. * The calling plug-in should include this statement,
  873. * if necessary, in the $additional_sql parameter
  874. *
  875. * $sql[] = "USE " . backquote($db_name);
  876. */
  877. /* Execute the SQL statements create above */
  878. $sql_len = count($sql);
  879. for ($i = 0; $i < $sql_len; ++$i) {
  880. PMA_importRunQuery($sql[$i], $sql[$i]);
  881. }
  882. /* No longer needed */
  883. unset($sql);
  884. /* Run the $additional_sql statements supplied by the caller plug-in */
  885. if ($additional_sql != null) {
  886. /* Clean the SQL first */
  887. $additional_sql_len = count($additional_sql);
  888. /**
  889. * Only match tables for now, because CREATE IF NOT EXISTS
  890. * syntax is lacking or nonexisting for views, triggers,
  891. * functions, and procedures.
  892. *
  893. * See: http://bugs.mysql.com/bug.php?id=15287
  894. *
  895. * To the best of my knowledge this is still an issue.
  896. *
  897. * $pattern = 'CREATE (TABLE|VIEW|TRIGGER|FUNCTION|PROCEDURE)';
  898. */
  899. $pattern = '/CREATE [^`]*(TABLE)/';
  900. $replacement = 'CREATE \\1 IF NOT EXISTS';
  901. /* Change CREATE statements to CREATE IF NOT EXISTS to support
  902. * inserting into existing structures
  903. */
  904. for ($i = 0; $i < $additional_sql_len; ++$i) {
  905. $additional_sql[$i] = preg_replace(
  906. $pattern,
  907. $replacement,
  908. $additional_sql[$i]
  909. );
  910. /* Execute the resulting statements */
  911. PMA_importRunQuery($additional_sql[$i], $additional_sql[$i]);
  912. }
  913. }
  914. if ($analyses != null) {
  915. $type_array = array(
  916. NONE => "NULL",
  917. VARCHAR => "varchar",
  918. INT => "int",
  919. DECIMAL => "decimal",
  920. BIGINT => "bigint",
  921. GEOMETRY => 'geometry'
  922. );
  923. /* TODO: Do more checking here to make sure they really are matched */
  924. if (count($tables) != count($analyses)) {
  925. exit();
  926. }
  927. /* Create SQL code to create the tables */
  928. $tempSQLStr = "";
  929. $num_tables = count($tables);
  930. for ($i = 0; $i < $num_tables; ++$i) {
  931. $num_cols = count($tables[$i][COL_NAMES]);
  932. $tempSQLStr = "CREATE TABLE IF NOT EXISTS " . PMA_Util::backquote($db_name)
  933. . '.' . PMA_Util::backquote($tables[$i][TBL_NAME]) . " (";
  934. for ($j = 0; $j < $num_cols; ++$j) {
  935. $size = $analyses[$i][SIZES][$j];
  936. if ((int)$size == 0) {
  937. $size = 10;
  938. }
  939. $tempSQLStr .= PMA_Util::backquote($tables[$i][COL_NAMES][$j]) . " "
  940. . $type_array[$analyses[$i][TYPES][$j]];
  941. if ($analyses[$i][TYPES][$j] != GEOMETRY) {
  942. $tempSQLStr .= "(" . $size . ")";
  943. }
  944. if ($j != (count($tables[$i][COL_NAMES]) - 1)) {
  945. $tempSQLStr .= ", ";
  946. }
  947. }
  948. $tempSQLStr .= ")"
  949. . (PMA_DRIZZLE ? "" : " DEFAULT CHARACTER SET " . $charset)
  950. . " COLLATE " . $collation . ";";
  951. /**
  952. * Each SQL statement is executed immediately
  953. * after it is formed so that we don't have
  954. * to store them in a (possibly large) buffer
  955. */
  956. PMA_importRunQuery($tempSQLStr, $tempSQLStr);
  957. }
  958. }
  959. /**
  960. * Create the SQL statements to insert all the data
  961. *
  962. * Only one insert query is formed for each table
  963. */
  964. $tempSQLStr = "";
  965. $col_count = 0;
  966. $num_tables = count($tables);
  967. for ($i = 0; $i < $num_tables; ++$i) {
  968. $num_cols = count($tables[$i][COL_NAMES]);
  969. $num_rows = count($tables[$i][ROWS]);
  970. $tempSQLStr = "INSERT INTO " . PMA_Util::backquote($db_name) . '.'
  971. . PMA_Util::backquote($tables[$i][TBL_NAME]) . " (";
  972. for ($m = 0; $m < $num_cols; ++$m) {
  973. $tempSQLStr .= PMA_Util::backquote($tables[$i][COL_NAMES][$m]);
  974. if ($m != ($num_cols - 1)) {
  975. $tempSQLStr .= ", ";
  976. }
  977. }
  978. $tempSQLStr .= ") VALUES ";
  979. for ($j = 0; $j < $num_rows; ++$j) {
  980. $tempSQLStr .= "(";
  981. for ($k = 0; $k < $num_cols; ++$k) {
  982. // If fully formatted SQL, no need to enclose
  983. // with aphostrophes, add shalshes etc.
  984. if ($analyses != null
  985. && isset($analyses[$i][FORMATTEDSQL][$col_count])
  986. && $analyses[$i][FORMATTEDSQL][$col_count] == true
  987. ) {
  988. $tempSQLStr .= (string) $tables[$i][ROWS][$j][$k];
  989. } else {
  990. if ($analyses != null) {
  991. $is_varchar = ($analyses[$i][TYPES][$col_count] === VARCHAR);
  992. } else {
  993. $is_varchar = ! is_numeric($tables[$i][ROWS][$j][$k]);
  994. }
  995. /* Don't put quotes around NULL fields */
  996. if (! strcmp($tables[$i][ROWS][$j][$k], 'NULL')) {
  997. $is_varchar = false;
  998. }
  999. $tempSQLStr .= (($is_varchar) ? "'" : "");
  1000. $tempSQLStr .= PMA_Util::sqlAddSlashes(
  1001. (string) $tables[$i][ROWS][$j][$k]
  1002. );
  1003. $tempSQLStr .= (($is_varchar) ? "'" : "");
  1004. }
  1005. if ($k != ($num_cols - 1)) {
  1006. $tempSQLStr .= ", ";
  1007. }
  1008. if ($col_count == ($num_cols - 1)) {
  1009. $col_count = 0;
  1010. } else {
  1011. $col_count++;
  1012. }
  1013. /* Delete the cell after we are done with it */
  1014. unset($tables[$i][ROWS][$j][$k]);
  1015. }
  1016. $tempSQLStr .= ")";
  1017. if ($j != ($num_rows - 1)) {
  1018. $tempSQLStr .= ",\n ";
  1019. }
  1020. $col_count = 0;
  1021. /* Delete the row after we are done with it */
  1022. unset($tables[$i][ROWS][$j]);
  1023. }
  1024. $tempSQLStr .= ";";
  1025. /**
  1026. * Each SQL statement is executed immediately
  1027. * after it is formed so that we don't have
  1028. * to store them in a (possibly large) buffer
  1029. */
  1030. PMA_importRunQuery($tempSQLStr, $tempSQLStr);
  1031. }
  1032. /* No longer needed */
  1033. unset($tempSQLStr);
  1034. /**
  1035. * A work in progress
  1036. */
  1037. /* Add the viewable structures from $additional_sql
  1038. * to $tables so they are also displayed
  1039. */
  1040. $view_pattern = '@VIEW `[^`]+`\.`([^`]+)@';
  1041. $table_pattern = '@CREATE TABLE IF NOT EXISTS `([^`]+)`@';
  1042. /* Check a third pattern to make sure its not a "USE `db_name`;" statement */
  1043. $regs = array();
  1044. $inTables = false;
  1045. $additional_sql_len = count($additional_sql);
  1046. for ($i = 0; $i < $additional_sql_len; ++$i) {
  1047. preg_match($view_pattern, $additional_sql[$i], $regs);
  1048. if (count($regs) == 0) {
  1049. preg_match($table_pattern, $additional_sql[$i], $regs);
  1050. }
  1051. if (count($regs)) {
  1052. for ($n = 0; $n < $num_tables; ++$n) {
  1053. if (! strcmp($regs[1], $tables[$n][TBL_NAME])) {
  1054. $inTables = true;
  1055. break;
  1056. }
  1057. }
  1058. if (! $inTables) {
  1059. $tables[] = array(TBL_NAME => $regs[1]);
  1060. }
  1061. }
  1062. /* Reset the array */
  1063. $regs = array();
  1064. $inTables = false;
  1065. }
  1066. $params = array('db' => (string)$db_name);
  1067. $db_url = 'db_structure.php' . PMA_generate_common_url($params);
  1068. $db_ops_url = 'db_operations.php' . PMA_generate_common_url($params);
  1069. $message = '<br /><br />';
  1070. $message .= '<strong>' . __('The following structures have either been created or altered. Here you can:') . '</strong><br />';
  1071. $message .= '<ul><li>' . __("View a structure's contents by clicking on its name") . '</li>';
  1072. $message .= '<li>' . __('Change any of its settings by clicking the corresponding "Options" link') . '</li>';
  1073. $message .= '<li>' . __('Edit structure by following the "Structure" link') . '</li>';
  1074. $message .= sprintf(
  1075. '<br /><li><a href="%s" title="%s">%s</a> (<a href="%s" title="%s">' . __('Options') . '</a>)</li>',
  1076. $db_url,
  1077. sprintf(__('Go to database: %s'), htmlspecialchars(PMA_Util::backquote($db_name))),
  1078. htmlspecialchars($db_name),
  1079. $db_ops_url,
  1080. sprintf(__('Edit settings for %s'), htmlspecialchars(PMA_Util::backquote($db_name)))
  1081. );
  1082. $message .= '<ul>';
  1083. unset($params);
  1084. $num_tables = count($tables);
  1085. for ($i = 0; $i < $num_tables; ++$i) {
  1086. $params = array(
  1087. 'db' => (string) $db_name,
  1088. 'table' => (string) $tables[$i][TBL_NAME]
  1089. );
  1090. $tbl_url = 'sql.php' . PMA_generate_common_url($params);
  1091. $tbl_struct_url = 'tbl_structure.php' . PMA_generate_common_url($params);
  1092. $tbl_ops_url = 'tbl_operations.php' . PMA_generate_common_url($params);
  1093. unset($params);
  1094. if (! PMA_Table::isView($db_name, $tables[$i][TBL_NAME])) {
  1095. $message .= sprintf(
  1096. '<li><a href="%s" title="%s">%s</a> (<a href="%s" title="%s">' . __('Structure') . '</a>) (<a href="%s" title="%s">' . __('Options') . '</a>)</li>',
  1097. $tbl_url,
  1098. sprintf(__('Go to table: %s'), htmlspecialchars(PMA_Util::backquote($tables[$i][TBL_NAME]))),
  1099. htmlspecialchars($tables[$i][TBL_NAME]),
  1100. $tbl_struct_url,
  1101. sprintf(__('Structure of %s'), htmlspecialchars(PMA_Util::backquote($tables[$i][TBL_NAME]))),
  1102. $tbl_ops_url,
  1103. sprintf(__('Edit settings for %s'), htmlspecialchars(PMA_Util::backquote($tables[$i][TBL_NAME])))
  1104. );
  1105. } else {
  1106. $message .= sprintf(
  1107. '<li><a href="%s" title="%s">%s</a></li>',
  1108. $tbl_url,
  1109. sprintf(__('Go to view: %s'), htmlspecialchars(PMA_Util::backquote($tables[$i][TBL_NAME]))),
  1110. htmlspecialchars($tables[$i][TBL_NAME])
  1111. );
  1112. }
  1113. }
  1114. $message .= '</ul></ul>';
  1115. global $import_notice;
  1116. $import_notice = $message;
  1117. unset($tables);
  1118. }
  1119. ?>