ExportSql.php 101 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878
  1. <?php
  2. /* vim: set expandtab sw=4 ts=4 sts=4: */
  3. /**
  4. * Set of functions used to build SQL dumps of tables
  5. *
  6. * @package PhpMyAdmin-Export
  7. * @subpackage SQL
  8. */
  9. namespace PhpMyAdmin\Plugins\Export;
  10. use PhpMyAdmin\Charsets;
  11. use PhpMyAdmin\DatabaseInterface;
  12. use PhpMyAdmin\Export;
  13. use PhpMyAdmin\Plugins\ExportPlugin;
  14. use PhpMyAdmin\Properties\Plugins\ExportPluginProperties;
  15. use PhpMyAdmin\Properties\Options\Groups\OptionsPropertyMainGroup;
  16. use PhpMyAdmin\Properties\Options\Groups\OptionsPropertyRootGroup;
  17. use PhpMyAdmin\Properties\Options\Groups\OptionsPropertySubgroup;
  18. use PhpMyAdmin\Properties\Options\Items\BoolPropertyItem;
  19. use PhpMyAdmin\Properties\Options\Items\MessageOnlyPropertyItem;
  20. use PhpMyAdmin\Properties\Options\Items\NumberPropertyItem;
  21. use PhpMyAdmin\Properties\Options\Items\RadioPropertyItem;
  22. use PhpMyAdmin\Properties\Options\Items\SelectPropertyItem;
  23. use PhpMyAdmin\Properties\Options\Items\TextPropertyItem;
  24. use PhpMyAdmin\Relation;
  25. use PhpMyAdmin\SqlParser\Components\CreateDefinition;
  26. use PhpMyAdmin\SqlParser\Context;
  27. use PhpMyAdmin\SqlParser\Parser;
  28. use PhpMyAdmin\SqlParser\Statements\SelectStatement;
  29. use PhpMyAdmin\SqlParser\Token;
  30. use PhpMyAdmin\Transformations;
  31. use PhpMyAdmin\Util;
  32. /**
  33. * Handles the export for the SQL class
  34. *
  35. * @package PhpMyAdmin-Export
  36. * @subpackage SQL
  37. */
  38. class ExportSql extends ExportPlugin
  39. {
  40. /**
  41. * Whether charset header was sent.
  42. *
  43. * @var boolean
  44. */
  45. private $_sent_charset = false;
  46. /**
  47. * Constructor
  48. */
  49. public function __construct()
  50. {
  51. parent::__construct();
  52. $this->setProperties();
  53. // Avoids undefined variables, use NULL so isset() returns false
  54. if (!isset($GLOBALS['sql_backquotes'])) {
  55. $GLOBALS['sql_backquotes'] = null;
  56. }
  57. }
  58. /**
  59. * Sets the export SQL properties
  60. *
  61. * @return void
  62. */
  63. protected function setProperties()
  64. {
  65. global $plugin_param;
  66. $hide_sql = false;
  67. $hide_structure = false;
  68. if ($plugin_param['export_type'] == 'table'
  69. && !$plugin_param['single_table']
  70. ) {
  71. $hide_structure = true;
  72. $hide_sql = true;
  73. }
  74. if (!$hide_sql) {
  75. $exportPluginProperties = new ExportPluginProperties();
  76. $exportPluginProperties->setText('SQL');
  77. $exportPluginProperties->setExtension('sql');
  78. $exportPluginProperties->setMimeType('text/x-sql');
  79. $exportPluginProperties->setOptionsText(__('Options'));
  80. // create the root group that will be the options field for
  81. // $exportPluginProperties
  82. // this will be shown as "Format specific options"
  83. $exportSpecificOptions = new OptionsPropertyRootGroup(
  84. "Format Specific Options"
  85. );
  86. // general options main group
  87. $generalOptions = new OptionsPropertyMainGroup("general_opts");
  88. // comments
  89. $subgroup = new OptionsPropertySubgroup("include_comments");
  90. $leaf = new BoolPropertyItem(
  91. 'include_comments',
  92. __(
  93. 'Display comments <i>(includes info such as export'
  94. . ' timestamp, PHP version, and server version)</i>'
  95. )
  96. );
  97. $subgroup->setSubgroupHeader($leaf);
  98. $leaf = new TextPropertyItem(
  99. 'header_comment',
  100. __('Additional custom header comment (\n splits lines):')
  101. );
  102. $subgroup->addProperty($leaf);
  103. $leaf = new BoolPropertyItem(
  104. 'dates',
  105. __(
  106. 'Include a timestamp of when databases were created, last'
  107. . ' updated, and last checked'
  108. )
  109. );
  110. $subgroup->addProperty($leaf);
  111. if (!empty($GLOBALS['cfgRelation']['relation'])) {
  112. $leaf = new BoolPropertyItem(
  113. 'relation',
  114. __('Display foreign key relationships')
  115. );
  116. $subgroup->addProperty($leaf);
  117. }
  118. if (!empty($GLOBALS['cfgRelation']['mimework'])) {
  119. $leaf = new BoolPropertyItem(
  120. 'mime',
  121. __('Display MIME types')
  122. );
  123. $subgroup->addProperty($leaf);
  124. }
  125. $generalOptions->addProperty($subgroup);
  126. // enclose in a transaction
  127. $leaf = new BoolPropertyItem(
  128. "use_transaction",
  129. __('Enclose export in a transaction')
  130. );
  131. $leaf->setDoc(
  132. array(
  133. 'programs',
  134. 'mysqldump',
  135. 'option_mysqldump_single-transaction',
  136. )
  137. );
  138. $generalOptions->addProperty($leaf);
  139. // disable foreign key checks
  140. $leaf = new BoolPropertyItem(
  141. "disable_fk",
  142. __('Disable foreign key checks')
  143. );
  144. $leaf->setDoc(
  145. array(
  146. 'manual_MySQL_Database_Administration',
  147. 'server-system-variables',
  148. 'sysvar_foreign_key_checks',
  149. )
  150. );
  151. $generalOptions->addProperty($leaf);
  152. // export views as tables
  153. $leaf = new BoolPropertyItem(
  154. "views_as_tables",
  155. __('Export views as tables')
  156. );
  157. $generalOptions->addProperty($leaf);
  158. // export metadata
  159. $leaf = new BoolPropertyItem(
  160. "metadata",
  161. __('Export metadata')
  162. );
  163. $generalOptions->addProperty($leaf);
  164. // compatibility maximization
  165. $compats = $GLOBALS['dbi']->getCompatibilities();
  166. if (count($compats) > 0) {
  167. $values = array();
  168. foreach ($compats as $val) {
  169. $values[$val] = $val;
  170. }
  171. $leaf = new SelectPropertyItem(
  172. "compatibility",
  173. __(
  174. 'Database system or older MySQL server to maximize output'
  175. . ' compatibility with:'
  176. )
  177. );
  178. $leaf->setValues($values);
  179. $leaf->setDoc(
  180. array(
  181. 'manual_MySQL_Database_Administration',
  182. 'Server_SQL_mode',
  183. )
  184. );
  185. $generalOptions->addProperty($leaf);
  186. unset($values);
  187. }
  188. // what to dump (structure/data/both)
  189. $subgroup = new OptionsPropertySubgroup(
  190. "dump_table", __("Dump table")
  191. );
  192. $leaf = new RadioPropertyItem('structure_or_data');
  193. $leaf->setValues(
  194. array(
  195. 'structure' => __('structure'),
  196. 'data' => __('data'),
  197. 'structure_and_data' => __('structure and data'),
  198. )
  199. );
  200. $subgroup->setSubgroupHeader($leaf);
  201. $generalOptions->addProperty($subgroup);
  202. // add the main group to the root group
  203. $exportSpecificOptions->addProperty($generalOptions);
  204. // structure options main group
  205. if (!$hide_structure) {
  206. $structureOptions = new OptionsPropertyMainGroup(
  207. "structure", __('Object creation options')
  208. );
  209. $structureOptions->setForce('data');
  210. // begin SQL Statements
  211. $subgroup = new OptionsPropertySubgroup();
  212. $leaf = new MessageOnlyPropertyItem(
  213. 'add_statements',
  214. __('Add statements:')
  215. );
  216. $subgroup->setSubgroupHeader($leaf);
  217. // server export options
  218. if ($plugin_param['export_type'] == 'server') {
  219. $leaf = new BoolPropertyItem(
  220. "drop_database",
  221. sprintf(__('Add %s statement'), '<code>DROP DATABASE IF EXISTS</code>')
  222. );
  223. $subgroup->addProperty($leaf);
  224. }
  225. if ($plugin_param['export_type'] == 'database') {
  226. $create_clause = '<code>CREATE DATABASE / USE</code>';
  227. $leaf = new BoolPropertyItem(
  228. 'create_database',
  229. sprintf(__('Add %s statement'), $create_clause)
  230. );
  231. $subgroup->addProperty($leaf);
  232. }
  233. if ($plugin_param['export_type'] == 'table') {
  234. $drop_clause = $GLOBALS['dbi']->getTable(
  235. $GLOBALS['db'],
  236. $GLOBALS['table']
  237. )->isView()
  238. ? '<code>DROP VIEW</code>'
  239. : '<code>DROP TABLE</code>';
  240. } else {
  241. $drop_clause = '<code>DROP TABLE / VIEW / PROCEDURE'
  242. . ' / FUNCTION / EVENT</code>';
  243. }
  244. $drop_clause .= '<code> / TRIGGER</code>';
  245. $leaf = new BoolPropertyItem(
  246. 'drop_table',
  247. sprintf(__('Add %s statement'), $drop_clause)
  248. );
  249. $subgroup->addProperty($leaf);
  250. $subgroup_create_table = new OptionsPropertySubgroup();
  251. // Add table structure option
  252. $leaf = new BoolPropertyItem(
  253. 'create_table',
  254. sprintf(__('Add %s statement'), '<code>CREATE TABLE</code>')
  255. );
  256. $subgroup_create_table->setSubgroupHeader($leaf);
  257. $leaf = new BoolPropertyItem(
  258. 'if_not_exists',
  259. '<code>IF NOT EXISTS</code> ' . __(
  260. '(less efficient as indexes will be generated during table '
  261. . 'creation)'
  262. )
  263. );
  264. $subgroup_create_table->addProperty($leaf);
  265. $leaf = new BoolPropertyItem(
  266. 'auto_increment',
  267. sprintf(__('%s value'), '<code>AUTO_INCREMENT</code>')
  268. );
  269. $subgroup_create_table->addProperty($leaf);
  270. $subgroup->addProperty($subgroup_create_table);
  271. // Add view option
  272. $leaf = new BoolPropertyItem(
  273. 'create_view',
  274. sprintf(__('Add %s statement'), '<code>CREATE VIEW</code>')
  275. );
  276. $subgroup->addProperty($leaf);
  277. $leaf = new BoolPropertyItem(
  278. 'procedure_function',
  279. sprintf(
  280. __('Add %s statement'),
  281. '<code>CREATE PROCEDURE / FUNCTION / EVENT</code>'
  282. )
  283. );
  284. $subgroup->addProperty($leaf);
  285. // Add triggers option
  286. $leaf = new BoolPropertyItem(
  287. 'create_trigger',
  288. sprintf(__('Add %s statement'), '<code>CREATE TRIGGER</code>')
  289. );
  290. $subgroup->addProperty($leaf);
  291. $structureOptions->addProperty($subgroup);
  292. $leaf = new BoolPropertyItem(
  293. "backquotes",
  294. __(
  295. 'Enclose table and column names with backquotes '
  296. . '<i>(Protects column and table names formed with'
  297. . ' special characters or keywords)</i>'
  298. )
  299. );
  300. $structureOptions->addProperty($leaf);
  301. // add the main group to the root group
  302. $exportSpecificOptions->addProperty($structureOptions);
  303. }
  304. // begin Data options
  305. $dataOptions = new OptionsPropertyMainGroup(
  306. "data", __('Data creation options')
  307. );
  308. $dataOptions->setForce('structure');
  309. $leaf = new BoolPropertyItem(
  310. "truncate",
  311. __('Truncate table before insert')
  312. );
  313. $dataOptions->addProperty($leaf);
  314. // begin SQL Statements
  315. $subgroup = new OptionsPropertySubgroup();
  316. $leaf = new MessageOnlyPropertyItem(
  317. __('Instead of <code>INSERT</code> statements, use:')
  318. );
  319. $subgroup->setSubgroupHeader($leaf);
  320. $leaf = new BoolPropertyItem(
  321. "delayed",
  322. __('<code>INSERT DELAYED</code> statements')
  323. );
  324. $leaf->setDoc(
  325. array(
  326. 'manual_MySQL_Database_Administration',
  327. 'insert_delayed'
  328. )
  329. );
  330. $subgroup->addProperty($leaf);
  331. $leaf = new BoolPropertyItem(
  332. "ignore",
  333. __('<code>INSERT IGNORE</code> statements')
  334. );
  335. $leaf->setDoc(
  336. array(
  337. 'manual_MySQL_Database_Administration',
  338. 'insert',
  339. )
  340. );
  341. $subgroup->addProperty($leaf);
  342. $dataOptions->addProperty($subgroup);
  343. // Function to use when dumping dat
  344. $leaf = new SelectPropertyItem(
  345. "type",
  346. __('Function to use when dumping data:')
  347. );
  348. $leaf->setValues(
  349. array(
  350. 'INSERT' => 'INSERT',
  351. 'UPDATE' => 'UPDATE',
  352. 'REPLACE' => 'REPLACE',
  353. )
  354. );
  355. $dataOptions->addProperty($leaf);
  356. /* Syntax to use when inserting data */
  357. $subgroup = new OptionsPropertySubgroup();
  358. $leaf = new MessageOnlyPropertyItem(
  359. null,
  360. __('Syntax to use when inserting data:')
  361. );
  362. $subgroup->setSubgroupHeader($leaf);
  363. $leaf = new RadioPropertyItem(
  364. "insert_syntax",
  365. __('<code>INSERT IGNORE</code> statements')
  366. );
  367. $leaf->setValues(
  368. array(
  369. 'complete' => __(
  370. 'include column names in every <code>INSERT</code> statement'
  371. . ' <br /> &nbsp; &nbsp; &nbsp; Example: <code>INSERT INTO'
  372. . ' tbl_name (col_A,col_B,col_C) VALUES (1,2,3)</code>'
  373. ),
  374. 'extended' => __(
  375. 'insert multiple rows in every <code>INSERT</code> statement'
  376. . '<br /> &nbsp; &nbsp; &nbsp; Example: <code>INSERT INTO'
  377. . ' tbl_name VALUES (1,2,3), (4,5,6), (7,8,9)</code>'
  378. ),
  379. 'both' => __(
  380. 'both of the above<br /> &nbsp; &nbsp; &nbsp; Example:'
  381. . ' <code>INSERT INTO tbl_name (col_A,col_B,col_C) VALUES'
  382. . ' (1,2,3), (4,5,6), (7,8,9)</code>'
  383. ),
  384. 'none' => __(
  385. 'neither of the above<br /> &nbsp; &nbsp; &nbsp; Example:'
  386. . ' <code>INSERT INTO tbl_name VALUES (1,2,3)</code>'
  387. ),
  388. )
  389. );
  390. $subgroup->addProperty($leaf);
  391. $dataOptions->addProperty($subgroup);
  392. // Max length of query
  393. $leaf = new NumberPropertyItem(
  394. "max_query_size",
  395. __('Maximal length of created query')
  396. );
  397. $dataOptions->addProperty($leaf);
  398. // Dump binary columns in hexadecimal
  399. $leaf = new BoolPropertyItem(
  400. "hex_for_binary",
  401. __(
  402. 'Dump binary columns in hexadecimal notation'
  403. . ' <i>(for example, "abc" becomes 0x616263)</i>'
  404. )
  405. );
  406. $dataOptions->addProperty($leaf);
  407. // Dump time in UTC
  408. $leaf = new BoolPropertyItem(
  409. "utc_time",
  410. __(
  411. 'Dump TIMESTAMP columns in UTC <i>(enables TIMESTAMP columns'
  412. . ' to be dumped and reloaded between servers in different'
  413. . ' time zones)</i>'
  414. )
  415. );
  416. $dataOptions->addProperty($leaf);
  417. // add the main group to the root group
  418. $exportSpecificOptions->addProperty($dataOptions);
  419. // set the options for the export plugin property item
  420. $exportPluginProperties->setOptions($exportSpecificOptions);
  421. $this->properties = $exportPluginProperties;
  422. }
  423. }
  424. /**
  425. * Generates SQL for routines export
  426. *
  427. * @param string $db Database
  428. * @param array $aliases Aliases of db/table/columns
  429. * @param string $type Type of exported routine
  430. * @param string $name Verbose name of exported routine
  431. * @param array $routines List of routines to export
  432. * @param string $delimiter Delimiter to use in SQL
  433. *
  434. * @return string SQL query
  435. */
  436. protected function _exportRoutineSQL(
  437. $db, array $aliases, $type, $name, array $routines, $delimiter
  438. ) {
  439. global $crlf;
  440. $text = $this->_exportComment()
  441. . $this->_exportComment($name)
  442. . $this->_exportComment();
  443. $used_alias = false;
  444. $proc_query = '';
  445. foreach ($routines as $routine) {
  446. if (!empty($GLOBALS['sql_drop_table'])) {
  447. $proc_query .= 'DROP ' . $type . ' IF EXISTS '
  448. . Util::backquote($routine)
  449. . $delimiter . $crlf;
  450. }
  451. $create_query = $this->replaceWithAliases(
  452. $GLOBALS['dbi']->getDefinition($db, $type, $routine),
  453. $aliases,
  454. $db,
  455. '',
  456. $flag
  457. );
  458. // One warning per database
  459. if ($flag) {
  460. $used_alias = true;
  461. }
  462. $proc_query .= $create_query . $delimiter . $crlf . $crlf;
  463. }
  464. if ($used_alias) {
  465. $text .= $this->_exportComment(
  466. __('It appears your database uses routines;')
  467. )
  468. . $this->_exportComment(
  469. __('alias export may not work reliably in all cases.')
  470. )
  471. . $this->_exportComment();
  472. }
  473. $text .= $proc_query;
  474. return $text;
  475. }
  476. /**
  477. * Exports routines (procedures and functions)
  478. *
  479. * @param string $db Database
  480. * @param array $aliases Aliases of db/table/columns
  481. *
  482. * @return bool Whether it succeeded
  483. */
  484. public function exportRoutines($db, array $aliases = array())
  485. {
  486. global $crlf;
  487. $db_alias = $db;
  488. $this->initAlias($aliases, $db_alias);
  489. $text = '';
  490. $delimiter = '$$';
  491. $procedure_names = $GLOBALS['dbi']
  492. ->getProceduresOrFunctions($db, 'PROCEDURE');
  493. $function_names = $GLOBALS['dbi']->getProceduresOrFunctions($db, 'FUNCTION');
  494. if ($procedure_names || $function_names) {
  495. $text .= $crlf
  496. . 'DELIMITER ' . $delimiter . $crlf;
  497. if ($procedure_names) {
  498. $text .= $this->_exportRoutineSQL(
  499. $db,
  500. $aliases,
  501. 'PROCEDURE',
  502. __('Procedures'),
  503. $procedure_names,
  504. $delimiter
  505. );
  506. }
  507. if ($function_names) {
  508. $text .= $this->_exportRoutineSQL(
  509. $db,
  510. $aliases,
  511. 'FUNCTION',
  512. __('Functions'),
  513. $function_names,
  514. $delimiter
  515. );
  516. }
  517. $text .= 'DELIMITER ;' . $crlf;
  518. }
  519. if (!empty($text)) {
  520. return Export::outputHandler($text);
  521. }
  522. return false;
  523. }
  524. /**
  525. * Possibly outputs comment
  526. *
  527. * @param string $text Text of comment
  528. *
  529. * @return string The formatted comment
  530. */
  531. private function _exportComment($text = '')
  532. {
  533. if (isset($GLOBALS['sql_include_comments'])
  534. && $GLOBALS['sql_include_comments']
  535. ) {
  536. // see https://dev.mysql.com/doc/refman/5.0/en/ansi-diff-comments.html
  537. if (empty($text)) {
  538. return '--' . $GLOBALS['crlf'];
  539. }
  540. $lines = preg_split("/\\r\\n|\\r|\\n/", $text);
  541. $result = array();
  542. foreach ($lines as $line) {
  543. $result[] = '-- ' . $line . $GLOBALS['crlf'];
  544. }
  545. return implode('', $result);
  546. }
  547. return '';
  548. }
  549. /**
  550. * Possibly outputs CRLF
  551. *
  552. * @return string $crlf or nothing
  553. */
  554. private function _possibleCRLF()
  555. {
  556. if (isset($GLOBALS['sql_include_comments'])
  557. && $GLOBALS['sql_include_comments']
  558. ) {
  559. return $GLOBALS['crlf'];
  560. }
  561. return '';
  562. }
  563. /**
  564. * Outputs export footer
  565. *
  566. * @return bool Whether it succeeded
  567. */
  568. public function exportFooter()
  569. {
  570. global $crlf;
  571. $foot = '';
  572. if (isset($GLOBALS['sql_disable_fk'])) {
  573. $foot .= 'SET FOREIGN_KEY_CHECKS=1;' . $crlf;
  574. }
  575. if (isset($GLOBALS['sql_use_transaction'])) {
  576. $foot .= 'COMMIT;' . $crlf;
  577. }
  578. // restore connection settings
  579. if ($this->_sent_charset) {
  580. $foot .= $crlf
  581. . '/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;'
  582. . $crlf
  583. . '/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;'
  584. . $crlf
  585. . '/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;'
  586. . $crlf;
  587. $this->_sent_charset = false;
  588. }
  589. /* Restore timezone */
  590. if (isset($GLOBALS['sql_utc_time']) && $GLOBALS['sql_utc_time']) {
  591. $GLOBALS['dbi']->query('SET time_zone = "' . $GLOBALS['old_tz'] . '"');
  592. }
  593. return Export::outputHandler($foot);
  594. }
  595. /**
  596. * Outputs export header. It is the first method to be called, so all
  597. * the required variables are initialized here.
  598. *
  599. * @return bool Whether it succeeded
  600. */
  601. public function exportHeader()
  602. {
  603. global $crlf, $cfg;
  604. if (isset($GLOBALS['sql_compatibility'])) {
  605. $tmp_compat = $GLOBALS['sql_compatibility'];
  606. if ($tmp_compat == 'NONE') {
  607. $tmp_compat = '';
  608. }
  609. $GLOBALS['dbi']->tryQuery('SET SQL_MODE="' . $tmp_compat . '"');
  610. unset($tmp_compat);
  611. }
  612. $head = $this->_exportComment('phpMyAdmin SQL Dump')
  613. . $this->_exportComment('version ' . PMA_VERSION)
  614. . $this->_exportComment('https://www.phpmyadmin.net/')
  615. . $this->_exportComment();
  616. $host_string = __('Host:') . ' ' . $cfg['Server']['host'];
  617. if (!empty($cfg['Server']['port'])) {
  618. $host_string .= ':' . $cfg['Server']['port'];
  619. }
  620. $head .= $this->_exportComment($host_string);
  621. $head .= $this->_exportComment(
  622. __('Generation Time:') . ' '
  623. . Util::localisedDate()
  624. )
  625. . $this->_exportComment(
  626. __('Server version:') . ' ' . $GLOBALS['dbi']->getVersionString()
  627. )
  628. . $this->_exportComment(__('PHP Version:') . ' ' . phpversion())
  629. . $this->_possibleCRLF();
  630. if (isset($GLOBALS['sql_header_comment'])
  631. && !empty($GLOBALS['sql_header_comment'])
  632. ) {
  633. // '\n' is not a newline (like "\n" would be), it's the characters
  634. // backslash and n, as explained on the export interface
  635. $lines = explode('\n', $GLOBALS['sql_header_comment']);
  636. $head .= $this->_exportComment();
  637. foreach ($lines as $one_line) {
  638. $head .= $this->_exportComment($one_line);
  639. }
  640. $head .= $this->_exportComment();
  641. }
  642. if (isset($GLOBALS['sql_disable_fk'])) {
  643. $head .= 'SET FOREIGN_KEY_CHECKS=0;' . $crlf;
  644. }
  645. // We want exported AUTO_INCREMENT columns to have still same value,
  646. // do this only for recent MySQL exports
  647. if ((! isset($GLOBALS['sql_compatibility'])
  648. || $GLOBALS['sql_compatibility'] == 'NONE')
  649. ) {
  650. $head .= 'SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO";' . $crlf;
  651. }
  652. if (isset($GLOBALS['sql_use_transaction'])) {
  653. $head .= 'SET AUTOCOMMIT = 0;' . $crlf
  654. . 'START TRANSACTION;' . $crlf;
  655. }
  656. /* Change timezone if we should export timestamps in UTC */
  657. if (isset($GLOBALS['sql_utc_time']) && $GLOBALS['sql_utc_time']) {
  658. $head .= 'SET time_zone = "+00:00";' . $crlf;
  659. $GLOBALS['old_tz'] = $GLOBALS['dbi']
  660. ->fetchValue('SELECT @@session.time_zone');
  661. $GLOBALS['dbi']->query('SET time_zone = "+00:00"');
  662. }
  663. $head .= $this->_possibleCRLF();
  664. if (! empty($GLOBALS['asfile'])) {
  665. // we are saving as file, therefore we provide charset information
  666. // so that a utility like the mysql client can interpret
  667. // the file correctly
  668. if (isset($GLOBALS['charset'])
  669. && isset(Charsets::$mysql_charset_map[$GLOBALS['charset']])
  670. ) {
  671. // we got a charset from the export dialog
  672. $set_names = Charsets::$mysql_charset_map[$GLOBALS['charset']];
  673. } else {
  674. // by default we use the connection charset
  675. $set_names = Charsets::$mysql_charset_map['utf-8'];
  676. }
  677. if ($set_names == 'utf8' && $GLOBALS['dbi']->getVersion() > 50503) {
  678. $set_names = 'utf8mb4';
  679. }
  680. $head .= $crlf
  681. . '/*!40101 SET @OLD_CHARACTER_SET_CLIENT='
  682. . '@@CHARACTER_SET_CLIENT */;' . $crlf
  683. . '/*!40101 SET @OLD_CHARACTER_SET_RESULTS='
  684. . '@@CHARACTER_SET_RESULTS */;' . $crlf
  685. . '/*!40101 SET @OLD_COLLATION_CONNECTION='
  686. . '@@COLLATION_CONNECTION */;' . $crlf
  687. . '/*!40101 SET NAMES ' . $set_names . ' */;' . $crlf . $crlf;
  688. $this->_sent_charset = true;
  689. }
  690. return Export::outputHandler($head);
  691. }
  692. /**
  693. * Outputs CREATE DATABASE statement
  694. *
  695. * @param string $db Database name
  696. * @param string $export_type 'server', 'database', 'table'
  697. * @param string $db_alias Aliases of db
  698. *
  699. * @return bool Whether it succeeded
  700. */
  701. public function exportDBCreate($db, $export_type, $db_alias = '')
  702. {
  703. global $crlf;
  704. if (empty($db_alias)) {
  705. $db_alias = $db;
  706. }
  707. if (isset($GLOBALS['sql_compatibility'])) {
  708. $compat = $GLOBALS['sql_compatibility'];
  709. } else {
  710. $compat = 'NONE';
  711. }
  712. if (isset($GLOBALS['sql_drop_database'])) {
  713. if (!Export::outputHandler(
  714. 'DROP DATABASE IF EXISTS '
  715. . Util::backquoteCompat(
  716. $db_alias,
  717. $compat,
  718. isset($GLOBALS['sql_backquotes'])
  719. )
  720. . ';' . $crlf
  721. )
  722. ) {
  723. return false;
  724. }
  725. }
  726. if ($export_type == 'database' && !isset($GLOBALS['sql_create_database'])) {
  727. return true;
  728. }
  729. $create_query = 'CREATE DATABASE IF NOT EXISTS '
  730. . Util::backquoteCompat(
  731. $db_alias,
  732. $compat,
  733. isset($GLOBALS['sql_backquotes'])
  734. );
  735. $collation = $GLOBALS['dbi']->getDbCollation($db);
  736. if (mb_strpos($collation, '_')) {
  737. $create_query .= ' DEFAULT CHARACTER SET '
  738. . mb_substr(
  739. $collation,
  740. 0,
  741. mb_strpos($collation, '_')
  742. )
  743. . ' COLLATE ' . $collation;
  744. } else {
  745. $create_query .= ' DEFAULT CHARACTER SET ' . $collation;
  746. }
  747. $create_query .= ';' . $crlf;
  748. if (!Export::outputHandler($create_query)) {
  749. return false;
  750. }
  751. return $this->_exportUseStatement($db_alias, $compat);
  752. }
  753. /**
  754. * Outputs USE statement
  755. *
  756. * @param string $db db to use
  757. * @param string $compat sql compatibility
  758. *
  759. * @return bool Whether it succeeded
  760. */
  761. private function _exportUseStatement($db, $compat)
  762. {
  763. global $crlf;
  764. if (isset($GLOBALS['sql_compatibility'])
  765. && $GLOBALS['sql_compatibility'] == 'NONE'
  766. ) {
  767. $result = Export::outputHandler(
  768. 'USE '
  769. . Util::backquoteCompat(
  770. $db,
  771. $compat,
  772. isset($GLOBALS['sql_backquotes'])
  773. )
  774. . ';' . $crlf
  775. );
  776. } else {
  777. $result = Export::outputHandler('USE ' . $db . ';' . $crlf);
  778. }
  779. return $result;
  780. }
  781. /**
  782. * Outputs database header
  783. *
  784. * @param string $db Database name
  785. * @param string $db_alias Alias of db
  786. *
  787. * @return bool Whether it succeeded
  788. */
  789. public function exportDBHeader($db, $db_alias = '')
  790. {
  791. if (empty($db_alias)) {
  792. $db_alias = $db;
  793. }
  794. if (isset($GLOBALS['sql_compatibility'])) {
  795. $compat = $GLOBALS['sql_compatibility'];
  796. } else {
  797. $compat = 'NONE';
  798. }
  799. $head = $this->_exportComment()
  800. . $this->_exportComment(
  801. __('Database:') . ' '
  802. . Util::backquoteCompat(
  803. $db_alias,
  804. $compat,
  805. isset($GLOBALS['sql_backquotes'])
  806. )
  807. )
  808. . $this->_exportComment();
  809. return Export::outputHandler($head);
  810. }
  811. /**
  812. * Outputs database footer
  813. *
  814. * @param string $db Database name
  815. *
  816. * @return bool Whether it succeeded
  817. */
  818. public function exportDBFooter($db)
  819. {
  820. global $crlf;
  821. $result = true;
  822. //add indexes to the sql dump file
  823. if (isset($GLOBALS['sql_indexes'])) {
  824. $result = Export::outputHandler($GLOBALS['sql_indexes']);
  825. unset($GLOBALS['sql_indexes']);
  826. }
  827. //add auto increments to the sql dump file
  828. if (isset($GLOBALS['sql_auto_increments'])) {
  829. $result = Export::outputHandler($GLOBALS['sql_auto_increments']);
  830. unset($GLOBALS['sql_auto_increments']);
  831. }
  832. //add constraints to the sql dump file
  833. if (isset($GLOBALS['sql_constraints'])) {
  834. $result = Export::outputHandler($GLOBALS['sql_constraints']);
  835. unset($GLOBALS['sql_constraints']);
  836. }
  837. return $result;
  838. }
  839. /**
  840. * Exports events
  841. *
  842. * @param string $db Database
  843. *
  844. * @return bool Whether it succeeded
  845. */
  846. public function exportEvents($db)
  847. {
  848. global $crlf;
  849. $text = '';
  850. $delimiter = '$$';
  851. $event_names = $GLOBALS['dbi']->fetchResult(
  852. "SELECT EVENT_NAME FROM information_schema.EVENTS WHERE"
  853. . " EVENT_SCHEMA= '" . $GLOBALS['dbi']->escapeString($db)
  854. . "';"
  855. );
  856. if ($event_names) {
  857. $text .= $crlf
  858. . "DELIMITER " . $delimiter . $crlf;
  859. $text .= $this->_exportComment()
  860. . $this->_exportComment(__('Events'))
  861. . $this->_exportComment();
  862. foreach ($event_names as $event_name) {
  863. if (!empty($GLOBALS['sql_drop_table'])) {
  864. $text .= "DROP EVENT "
  865. . Util::backquote($event_name)
  866. . $delimiter . $crlf;
  867. }
  868. $text .= $GLOBALS['dbi']->getDefinition($db, 'EVENT', $event_name)
  869. . $delimiter . $crlf . $crlf;
  870. }
  871. $text .= "DELIMITER ;" . $crlf;
  872. }
  873. if (!empty($text)) {
  874. return Export::outputHandler($text);
  875. }
  876. return false;
  877. }
  878. /**
  879. * Exports metadata from Configuration Storage
  880. *
  881. * @param string $db database being exported
  882. * @param string|array $tables table(s) being exported
  883. * @param array $metadataTypes types of metadata to export
  884. *
  885. * @return bool Whether it succeeded
  886. */
  887. public function exportMetadata(
  888. $db,
  889. $tables,
  890. array $metadataTypes
  891. ) {
  892. $cfgRelation = $this->relation->getRelationsParam();
  893. if (!isset($cfgRelation['db'])) {
  894. return true;
  895. }
  896. $comment = $this->_possibleCRLF()
  897. . $this->_possibleCRLF()
  898. . $this->_exportComment()
  899. . $this->_exportComment(__('Metadata'))
  900. . $this->_exportComment();
  901. if (!Export::outputHandler($comment)) {
  902. return false;
  903. }
  904. if (!$this->_exportUseStatement(
  905. $cfgRelation['db'],
  906. $GLOBALS['sql_compatibility']
  907. )
  908. ) {
  909. return false;
  910. }
  911. $r = true;
  912. if (is_array($tables)) {
  913. // export metadata for each table
  914. foreach ($tables as $table) {
  915. $r &= $this->_exportMetadata($db, $table, $metadataTypes);
  916. }
  917. // export metadata for the database
  918. $r &= $this->_exportMetadata($db, null, $metadataTypes);
  919. } else {
  920. // export metadata for single table
  921. $r &= $this->_exportMetadata($db, $tables, $metadataTypes);
  922. }
  923. return $r;
  924. }
  925. /**
  926. * Exports metadata from Configuration Storage
  927. *
  928. * @param string $db database being exported
  929. * @param string $table table being exported
  930. * @param array $metadataTypes types of metadata to export
  931. *
  932. * @return bool Whether it succeeded
  933. */
  934. private function _exportMetadata(
  935. $db,
  936. $table,
  937. array $metadataTypes
  938. ) {
  939. $cfgRelation = $this->relation->getRelationsParam();
  940. if (isset($table)) {
  941. $types = array(
  942. 'column_info' => 'db_name',
  943. 'table_uiprefs' => 'db_name',
  944. 'tracking' => 'db_name',
  945. );
  946. } else {
  947. $types = array(
  948. 'bookmark' => 'dbase',
  949. 'relation' => 'master_db',
  950. 'pdf_pages' => 'db_name',
  951. 'savedsearches' => 'db_name',
  952. 'central_columns' => 'db_name',
  953. );
  954. }
  955. $aliases = array();
  956. $comment = $this->_possibleCRLF()
  957. . $this->_exportComment();
  958. if (isset($table)) {
  959. $comment .= $this->_exportComment(
  960. sprintf(
  961. __('Metadata for table %s'),
  962. $table
  963. )
  964. );
  965. } else {
  966. $comment .= $this->_exportComment(
  967. sprintf(
  968. __('Metadata for database %s'),
  969. $db
  970. )
  971. );
  972. }
  973. $comment .= $this->_exportComment();
  974. if (!Export::outputHandler($comment)) {
  975. return false;
  976. }
  977. foreach ($types as $type => $dbNameColumn) {
  978. if (in_array($type, $metadataTypes) && isset($cfgRelation[$type])) {
  979. // special case, designer pages and their coordinates
  980. if ($type == 'pdf_pages') {
  981. $sql_query = "SELECT `page_nr`, `page_descr` FROM "
  982. . Util::backquote($cfgRelation['db'])
  983. . "." . Util::backquote($cfgRelation[$type])
  984. . " WHERE " . Util::backquote($dbNameColumn)
  985. . " = '" . $GLOBALS['dbi']->escapeString($db) . "'";
  986. $result = $GLOBALS['dbi']->fetchResult(
  987. $sql_query,
  988. 'page_nr',
  989. 'page_descr'
  990. );
  991. foreach ($result as $page => $name) {
  992. // insert row for pdf_page
  993. $sql_query_row = "SELECT `db_name`, `page_descr` FROM "
  994. . Util::backquote($cfgRelation['db'])
  995. . "." . Util::backquote(
  996. $cfgRelation[$type]
  997. )
  998. . " WHERE " . Util::backquote(
  999. $dbNameColumn
  1000. )
  1001. . " = '" . $GLOBALS['dbi']->escapeString($db) . "'"
  1002. . " AND `page_nr` = '" . intval($page) . "'";
  1003. if (!$this->exportData(
  1004. $cfgRelation['db'],
  1005. $cfgRelation[$type],
  1006. $GLOBALS['crlf'],
  1007. '',
  1008. $sql_query_row,
  1009. $aliases
  1010. )
  1011. ) {
  1012. return false;
  1013. }
  1014. $lastPage = $GLOBALS['crlf']
  1015. . "SET @LAST_PAGE = LAST_INSERT_ID();"
  1016. . $GLOBALS['crlf'];
  1017. if (!Export::outputHandler($lastPage)) {
  1018. return false;
  1019. }
  1020. $sql_query_coords = "SELECT `db_name`, `table_name`, "
  1021. . "'@LAST_PAGE' AS `pdf_page_number`, `x`, `y` FROM "
  1022. . Util::backquote($cfgRelation['db'])
  1023. . "." . Util::backquote(
  1024. $cfgRelation['table_coords']
  1025. )
  1026. . " WHERE `pdf_page_number` = '" . $page . "'";
  1027. $GLOBALS['exporting_metadata'] = true;
  1028. if (!$this->exportData(
  1029. $cfgRelation['db'],
  1030. $cfgRelation['table_coords'],
  1031. $GLOBALS['crlf'],
  1032. '',
  1033. $sql_query_coords,
  1034. $aliases
  1035. )
  1036. ) {
  1037. $GLOBALS['exporting_metadata'] = false;
  1038. return false;
  1039. }
  1040. $GLOBALS['exporting_metadata'] = false;
  1041. }
  1042. continue;
  1043. }
  1044. // remove auto_incrementing id field for some tables
  1045. if ($type == 'bookmark') {
  1046. $sql_query = "SELECT `dbase`, `user`, `label`, `query` FROM ";
  1047. } elseif ($type == 'column_info') {
  1048. $sql_query = "SELECT `db_name`, `table_name`, `column_name`,"
  1049. . " `comment`, `mimetype`, `transformation`,"
  1050. . " `transformation_options`, `input_transformation`,"
  1051. . " `input_transformation_options` FROM";
  1052. } elseif ($type == 'savedsearches') {
  1053. $sql_query = "SELECT `username`, `db_name`, `search_name`,"
  1054. . " `search_data` FROM";
  1055. } else {
  1056. $sql_query = "SELECT * FROM ";
  1057. }
  1058. $sql_query .= Util::backquote($cfgRelation['db'])
  1059. . '.' . Util::backquote($cfgRelation[$type])
  1060. . " WHERE " . Util::backquote($dbNameColumn)
  1061. . " = '" . $GLOBALS['dbi']->escapeString($db) . "'";
  1062. if (isset($table)) {
  1063. $sql_query .= " AND `table_name` = '"
  1064. . $GLOBALS['dbi']->escapeString($table) . "'";
  1065. }
  1066. if (!$this->exportData(
  1067. $cfgRelation['db'],
  1068. $cfgRelation[$type],
  1069. $GLOBALS['crlf'],
  1070. '',
  1071. $sql_query,
  1072. $aliases
  1073. )
  1074. ) {
  1075. return false;
  1076. }
  1077. }
  1078. }
  1079. return true;
  1080. }
  1081. /**
  1082. * Returns a stand-in CREATE definition to resolve view dependencies
  1083. *
  1084. * @param string $db the database name
  1085. * @param string $view the view name
  1086. * @param string $crlf the end of line sequence
  1087. * @param array $aliases Aliases of db/table/columns
  1088. *
  1089. * @return string resulting definition
  1090. */
  1091. public function getTableDefStandIn($db, $view, $crlf, $aliases = array())
  1092. {
  1093. $db_alias = $db;
  1094. $view_alias = $view;
  1095. $this->initAlias($aliases, $db_alias, $view_alias);
  1096. $create_query = '';
  1097. if (!empty($GLOBALS['sql_drop_table'])) {
  1098. $create_query .= 'DROP VIEW IF EXISTS '
  1099. . Util::backquote($view_alias)
  1100. . ';' . $crlf;
  1101. }
  1102. $create_query .= 'CREATE TABLE ';
  1103. if (isset($GLOBALS['sql_if_not_exists'])
  1104. && $GLOBALS['sql_if_not_exists']
  1105. ) {
  1106. $create_query .= 'IF NOT EXISTS ';
  1107. }
  1108. $create_query .= Util::backquote($view_alias) . ' (' . $crlf;
  1109. $tmp = array();
  1110. $columns = $GLOBALS['dbi']->getColumnsFull($db, $view);
  1111. foreach ($columns as $column_name => $definition) {
  1112. $col_alias = $column_name;
  1113. if (!empty($aliases[$db]['tables'][$view]['columns'][$col_alias])) {
  1114. $col_alias = $aliases[$db]['tables'][$view]['columns'][$col_alias];
  1115. }
  1116. $tmp[] = Util::backquote($col_alias) . ' ' .
  1117. $definition['Type'] . $crlf;
  1118. }
  1119. $create_query .= implode(',', $tmp) . ');' . $crlf;
  1120. return ($create_query);
  1121. }
  1122. /**
  1123. * Returns CREATE definition that matches $view's structure
  1124. *
  1125. * @param string $db the database name
  1126. * @param string $view the view name
  1127. * @param string $crlf the end of line sequence
  1128. * @param bool $add_semicolon whether to add semicolon and end-of-line at
  1129. * the end
  1130. * @param array $aliases Aliases of db/table/columns
  1131. *
  1132. * @return string resulting schema
  1133. */
  1134. private function _getTableDefForView(
  1135. $db,
  1136. $view,
  1137. $crlf,
  1138. $add_semicolon = true,
  1139. array $aliases = array()
  1140. ) {
  1141. $db_alias = $db;
  1142. $view_alias = $view;
  1143. $this->initAlias($aliases, $db_alias, $view_alias);
  1144. $create_query = "CREATE TABLE";
  1145. if (isset($GLOBALS['sql_if_not_exists'])) {
  1146. $create_query .= " IF NOT EXISTS ";
  1147. }
  1148. $create_query .= Util::backquote($view_alias) . "(" . $crlf;
  1149. $columns = $GLOBALS['dbi']->getColumns($db, $view, null, true);
  1150. $firstCol = true;
  1151. foreach ($columns as $column) {
  1152. $col_alias = $column['Field'];
  1153. if (!empty($aliases[$db]['tables'][$view]['columns'][$col_alias])) {
  1154. $col_alias = $aliases[$db]['tables'][$view]['columns'][$col_alias];
  1155. }
  1156. $extracted_columnspec = Util::extractColumnSpec(
  1157. $column['Type']
  1158. );
  1159. if (!$firstCol) {
  1160. $create_query .= "," . $crlf;
  1161. }
  1162. $create_query .= " " . Util::backquote($col_alias);
  1163. $create_query .= " " . $column['Type'];
  1164. if ($extracted_columnspec['can_contain_collation']
  1165. && !empty($column['Collation'])
  1166. ) {
  1167. $create_query .= " COLLATE " . $column['Collation'];
  1168. }
  1169. if ($column['Null'] == 'NO') {
  1170. $create_query .= " NOT NULL";
  1171. }
  1172. if (isset($column['Default'])) {
  1173. $create_query .= " DEFAULT '"
  1174. . $GLOBALS['dbi']->escapeString($column['Default']) . "'";
  1175. } else {
  1176. if ($column['Null'] == 'YES') {
  1177. $create_query .= " DEFAULT NULL";
  1178. }
  1179. }
  1180. if (!empty($column['Comment'])) {
  1181. $create_query .= " COMMENT '"
  1182. . $GLOBALS['dbi']->escapeString($column['Comment']) . "'";
  1183. }
  1184. $firstCol = false;
  1185. }
  1186. $create_query .= $crlf . ")" . ($add_semicolon ? ';' : '') . $crlf;
  1187. if (isset($GLOBALS['sql_compatibility'])) {
  1188. $compat = $GLOBALS['sql_compatibility'];
  1189. } else {
  1190. $compat = 'NONE';
  1191. }
  1192. if ($compat == 'MSSQL') {
  1193. $create_query = $this->_makeCreateTableMSSQLCompatible(
  1194. $create_query
  1195. );
  1196. }
  1197. return $create_query;
  1198. }
  1199. /**
  1200. * Returns $table's CREATE definition
  1201. *
  1202. * @param string $db the database name
  1203. * @param string $table the table name
  1204. * @param string $crlf the end of line sequence
  1205. * @param string $error_url the url to go back in case
  1206. * of error
  1207. * @param bool $show_dates whether to include creation/
  1208. * update/check dates
  1209. * @param bool $add_semicolon whether to add semicolon and
  1210. * end-of-line at the end
  1211. * @param bool $view whether we're handling a view
  1212. * @param bool $update_indexes_increments whether we need to update
  1213. * two global variables
  1214. * @param array $aliases Aliases of db/table/columns
  1215. *
  1216. * @return string resulting schema
  1217. */
  1218. public function getTableDef(
  1219. $db,
  1220. $table,
  1221. $crlf,
  1222. $error_url,
  1223. $show_dates = false,
  1224. $add_semicolon = true,
  1225. $view = false,
  1226. $update_indexes_increments = true,
  1227. array $aliases = array()
  1228. ) {
  1229. global $sql_drop_table, $sql_backquotes, $sql_constraints,
  1230. $sql_constraints_query, $sql_indexes, $sql_indexes_query,
  1231. $sql_auto_increments, $sql_drop_foreign_keys;
  1232. $db_alias = $db;
  1233. $table_alias = $table;
  1234. $this->initAlias($aliases, $db_alias, $table_alias);
  1235. $schema_create = '';
  1236. $auto_increment = '';
  1237. $new_crlf = $crlf;
  1238. if (isset($GLOBALS['sql_compatibility'])) {
  1239. $compat = $GLOBALS['sql_compatibility'];
  1240. } else {
  1241. $compat = 'NONE';
  1242. }
  1243. // need to use PhpMyAdmin\DatabaseInterface::QUERY_STORE
  1244. // with $GLOBALS['dbi']->numRows() in mysqli
  1245. $result = $GLOBALS['dbi']->tryQuery(
  1246. 'SHOW TABLE STATUS FROM ' . Util::backquote($db)
  1247. . ' WHERE Name = \'' . $GLOBALS['dbi']->escapeString($table) . '\'',
  1248. DatabaseInterface::CONNECT_USER,
  1249. DatabaseInterface::QUERY_STORE
  1250. );
  1251. if ($result != false) {
  1252. if ($GLOBALS['dbi']->numRows($result) > 0) {
  1253. $tmpres = $GLOBALS['dbi']->fetchAssoc($result);
  1254. // Here we optionally add the AUTO_INCREMENT next value,
  1255. // but starting with MySQL 5.0.24, the clause is already included
  1256. // in SHOW CREATE TABLE so we'll remove it below
  1257. if (isset($GLOBALS['sql_auto_increment'])
  1258. && !empty($tmpres['Auto_increment'])
  1259. ) {
  1260. $auto_increment .= ' AUTO_INCREMENT='
  1261. . $tmpres['Auto_increment'] . ' ';
  1262. }
  1263. if ($show_dates
  1264. && isset($tmpres['Create_time'])
  1265. && !empty($tmpres['Create_time'])
  1266. ) {
  1267. $schema_create .= $this->_exportComment(
  1268. __('Creation:') . ' '
  1269. . Util::localisedDate(
  1270. strtotime($tmpres['Create_time'])
  1271. )
  1272. );
  1273. $new_crlf = $this->_exportComment() . $crlf;
  1274. }
  1275. if ($show_dates
  1276. && isset($tmpres['Update_time'])
  1277. && !empty($tmpres['Update_time'])
  1278. ) {
  1279. $schema_create .= $this->_exportComment(
  1280. __('Last update:') . ' '
  1281. . Util::localisedDate(
  1282. strtotime($tmpres['Update_time'])
  1283. )
  1284. );
  1285. $new_crlf = $this->_exportComment() . $crlf;
  1286. }
  1287. if ($show_dates
  1288. && isset($tmpres['Check_time'])
  1289. && !empty($tmpres['Check_time'])
  1290. ) {
  1291. $schema_create .= $this->_exportComment(
  1292. __('Last check:') . ' '
  1293. . Util::localisedDate(
  1294. strtotime($tmpres['Check_time'])
  1295. )
  1296. );
  1297. $new_crlf = $this->_exportComment() . $crlf;
  1298. }
  1299. }
  1300. $GLOBALS['dbi']->freeResult($result);
  1301. }
  1302. $schema_create .= $new_crlf;
  1303. // no need to generate a DROP VIEW here, it was done earlier
  1304. if (!empty($sql_drop_table)
  1305. && !$GLOBALS['dbi']->getTable($db, $table)->isView()
  1306. ) {
  1307. $schema_create .= 'DROP TABLE IF EXISTS '
  1308. . Util::backquote($table_alias, $sql_backquotes) . ';'
  1309. . $crlf;
  1310. }
  1311. // Complete table dump,
  1312. // Whether to quote table and column names or not
  1313. if ($sql_backquotes) {
  1314. $GLOBALS['dbi']->query('SET SQL_QUOTE_SHOW_CREATE = 1');
  1315. } else {
  1316. $GLOBALS['dbi']->query('SET SQL_QUOTE_SHOW_CREATE = 0');
  1317. }
  1318. // I don't see the reason why this unbuffered query could cause problems,
  1319. // because SHOW CREATE TABLE returns only one row, and we free the
  1320. // results below. Nonetheless, we got 2 user reports about this
  1321. // (see bug 1562533) so I removed the unbuffered mode.
  1322. // $result = $GLOBALS['dbi']->query('SHOW CREATE TABLE ' . backquote($db)
  1323. // . '.' . backquote($table), null, DatabaseInterface::QUERY_UNBUFFERED);
  1324. //
  1325. // Note: SHOW CREATE TABLE, at least in MySQL 5.1.23, does not
  1326. // produce a displayable result for the default value of a BIT
  1327. // column, nor does the mysqldump command. See MySQL bug 35796
  1328. $GLOBALS['dbi']->tryQuery('USE ' . Util::backquote($db));
  1329. $result = $GLOBALS['dbi']->tryQuery(
  1330. 'SHOW CREATE TABLE ' . Util::backquote($db) . '.'
  1331. . Util::backquote($table)
  1332. );
  1333. // an error can happen, for example the table is crashed
  1334. $tmp_error = $GLOBALS['dbi']->getError();
  1335. if ($tmp_error) {
  1336. $message = sprintf(__('Error reading structure for table %s:'), "$db.$table");
  1337. $message .= ' ' . $tmp_error;
  1338. if (! defined('TESTSUITE')) {
  1339. trigger_error($message, E_USER_ERROR);
  1340. }
  1341. return $this->_exportComment($message);
  1342. }
  1343. // Old mode is stored so it can be restored once exporting is done.
  1344. $old_mode = Context::$MODE;
  1345. $warning = '';
  1346. if ($result != false && ($row = $GLOBALS['dbi']->fetchRow($result))) {
  1347. $create_query = $row[1];
  1348. unset($row);
  1349. // Convert end of line chars to one that we want (note that MySQL
  1350. // doesn't return query it will accept in all cases)
  1351. if (mb_strpos($create_query, "(\r\n ")) {
  1352. $create_query = str_replace("\r\n", $crlf, $create_query);
  1353. } elseif (mb_strpos($create_query, "(\n ")) {
  1354. $create_query = str_replace("\n", $crlf, $create_query);
  1355. } elseif (mb_strpos($create_query, "(\r ")) {
  1356. $create_query = str_replace("\r", $crlf, $create_query);
  1357. }
  1358. /*
  1359. * Drop database name from VIEW creation.
  1360. *
  1361. * This is a bit tricky, but we need to issue SHOW CREATE TABLE with
  1362. * database name, but we don't want name to show up in CREATE VIEW
  1363. * statement.
  1364. */
  1365. if ($view) {
  1366. $create_query = preg_replace(
  1367. '/' . preg_quote(Util::backquote($db), '/') . '\./',
  1368. '',
  1369. $create_query
  1370. );
  1371. }
  1372. // Substitute aliases in `CREATE` query.
  1373. $create_query = $this->replaceWithAliases(
  1374. $create_query,
  1375. $aliases,
  1376. $db,
  1377. $table,
  1378. $flag
  1379. );
  1380. // One warning per view.
  1381. if ($flag && $view) {
  1382. $warning = $this->_exportComment()
  1383. . $this->_exportComment(
  1384. __('It appears your database uses views;')
  1385. )
  1386. . $this->_exportComment(
  1387. __('alias export may not work reliably in all cases.')
  1388. )
  1389. . $this->_exportComment();
  1390. }
  1391. // Adding IF NOT EXISTS, if required.
  1392. if (isset($GLOBALS['sql_if_not_exists'])) {
  1393. $create_query = preg_replace(
  1394. '/^CREATE TABLE/',
  1395. 'CREATE TABLE IF NOT EXISTS',
  1396. $create_query
  1397. );
  1398. }
  1399. // Making the query MSSQL compatible.
  1400. if ($compat == 'MSSQL') {
  1401. $create_query = $this->_makeCreateTableMSSQLCompatible(
  1402. $create_query
  1403. );
  1404. }
  1405. // Views have no constraints, indexes, etc. They do not require any
  1406. // analysis.
  1407. if (!$view) {
  1408. if (empty($sql_backquotes)) {
  1409. // Option "Enclose table and column names with backquotes"
  1410. // was checked.
  1411. Context::$MODE |= Context::SQL_MODE_NO_ENCLOSING_QUOTES;
  1412. }
  1413. // Using appropriate quotes.
  1414. if (($compat === 'MSSQL') || ($sql_backquotes === '"')) {
  1415. Context::$MODE |= Context::SQL_MODE_ANSI_QUOTES;
  1416. }
  1417. }
  1418. /**
  1419. * Parser used for analysis.
  1420. *
  1421. * @var Parser
  1422. */
  1423. $parser = new Parser($create_query);
  1424. /**
  1425. * `CREATE TABLE` statement.
  1426. *
  1427. * @var SelectStatement
  1428. */
  1429. $statement = $parser->statements[0];
  1430. if (!empty($statement->entityOptions)) {
  1431. $engine = $statement->entityOptions->has('ENGINE');
  1432. } else {
  1433. $engine = '';
  1434. }
  1435. /* Avoid operation on ARCHIVE tables as those can not be altered */
  1436. if (!empty($statement->fields) && (empty($engine) || strtoupper($engine) != 'ARCHIVE')) {
  1437. /**
  1438. * Fragments containining definition of each constraint.
  1439. *
  1440. * @var array
  1441. */
  1442. $constraints = array();
  1443. /**
  1444. * Fragments containining definition of each index.
  1445. *
  1446. * @var array
  1447. */
  1448. $indexes = array();
  1449. /**
  1450. * Fragments containining definition of each FULLTEXT index.
  1451. *
  1452. * @var array
  1453. */
  1454. $indexes_fulltext = array();
  1455. /**
  1456. * Fragments containining definition of each foreign key that will
  1457. * be dropped.
  1458. *
  1459. * @var array
  1460. */
  1461. $dropped = array();
  1462. /**
  1463. * Fragment containining definition of the `AUTO_INCREMENT`.
  1464. *
  1465. * @var array
  1466. */
  1467. $auto_increment = array();
  1468. // Scanning each field of the `CREATE` statement to fill the arrays
  1469. // above.
  1470. // If the field is used in any of the arrays above, it is removed
  1471. // from the original definition.
  1472. // Also, AUTO_INCREMENT attribute is removed.
  1473. /** @var CreateDefinition $field */
  1474. foreach ($statement->fields as $key => $field) {
  1475. if ($field->isConstraint) {
  1476. // Creating the parts that add constraints.
  1477. $constraints[] = $field::build($field);
  1478. unset($statement->fields[$key]);
  1479. } elseif (!empty($field->key)) {
  1480. // Creating the parts that add indexes (must not be
  1481. // constraints).
  1482. if ($field->key->type === 'FULLTEXT KEY') {
  1483. $indexes_fulltext[] = $field->build($field);
  1484. unset($statement->fields[$key]);
  1485. } else {
  1486. if (empty($GLOBALS['sql_if_not_exists'])) {
  1487. $indexes[] = str_replace(
  1488. 'COMMENT=\'', 'COMMENT \'', $field::build($field)
  1489. );
  1490. unset($statement->fields[$key]);
  1491. }
  1492. }
  1493. }
  1494. // Creating the parts that drop foreign keys.
  1495. if (!empty($field->key)) {
  1496. if ($field->key->type === 'FOREIGN KEY') {
  1497. $dropped[] = 'FOREIGN KEY ' . Context::escape(
  1498. $field->name
  1499. );
  1500. unset($statement->fields[$key]);
  1501. }
  1502. }
  1503. // Dropping AUTO_INCREMENT.
  1504. if (!empty($field->options)) {
  1505. if ($field->options->has('AUTO_INCREMENT')
  1506. && empty($GLOBALS['sql_if_not_exists'])
  1507. ) {
  1508. $auto_increment[] = $field::build($field);
  1509. $field->options->remove('AUTO_INCREMENT');
  1510. }
  1511. }
  1512. }
  1513. /**
  1514. * The header of the `ALTER` statement (`ALTER TABLE tbl`).
  1515. *
  1516. * @var string
  1517. */
  1518. $alter_header = 'ALTER TABLE ' .
  1519. Util::backquoteCompat(
  1520. $table_alias,
  1521. $compat,
  1522. $sql_backquotes
  1523. );
  1524. /**
  1525. * The footer of the `ALTER` statement (usually ';')
  1526. *
  1527. * @var string
  1528. */
  1529. $alter_footer = ';' . $crlf;
  1530. // Generating constraints-related query.
  1531. if (!empty($constraints)) {
  1532. $sql_constraints_query = $alter_header . $crlf . ' ADD '
  1533. . implode(',' . $crlf . ' ADD ', $constraints)
  1534. . $alter_footer;
  1535. $sql_constraints = $this->generateComment(
  1536. $crlf,
  1537. $sql_constraints,
  1538. __('Constraints for dumped tables'),
  1539. __('Constraints for table'),
  1540. $table_alias,
  1541. $compat
  1542. ) . $sql_constraints_query;
  1543. }
  1544. // Generating indexes-related query.
  1545. $sql_indexes_query = '';
  1546. if (!empty($indexes)) {
  1547. $sql_indexes_query .= $alter_header . $crlf . ' ADD '
  1548. . implode(',' . $crlf . ' ADD ', $indexes)
  1549. . $alter_footer;
  1550. }
  1551. if (!empty($indexes_fulltext)) {
  1552. // InnoDB supports one FULLTEXT index creation at a time.
  1553. // So FULLTEXT indexes are created one-by-one after other
  1554. // indexes where created.
  1555. $sql_indexes_query .= $alter_header .
  1556. ' ADD ' . implode(
  1557. $alter_footer . $alter_header . ' ADD ',
  1558. $indexes_fulltext
  1559. ) . $alter_footer;
  1560. }
  1561. if ((!empty($indexes)) || (!empty($indexes_fulltext))) {
  1562. $sql_indexes = $this->generateComment(
  1563. $crlf,
  1564. $sql_indexes,
  1565. __('Indexes for dumped tables'),
  1566. __('Indexes for table'),
  1567. $table_alias,
  1568. $compat
  1569. ) . $sql_indexes_query;
  1570. }
  1571. // Generating drop foreign keys-related query.
  1572. if (!empty($dropped)) {
  1573. $sql_drop_foreign_keys = $alter_header . $crlf . ' DROP '
  1574. . implode(',' . $crlf . ' DROP ', $dropped)
  1575. . $alter_footer;
  1576. }
  1577. // Generating auto-increment-related query.
  1578. if ((! empty($auto_increment)) && ($update_indexes_increments)) {
  1579. $sql_auto_increments_query = $alter_header . $crlf . ' MODIFY '
  1580. . implode(',' . $crlf . ' MODIFY ', $auto_increment);
  1581. if (isset($GLOBALS['sql_auto_increment'])
  1582. && ($statement->entityOptions->has('AUTO_INCREMENT') !== false)
  1583. ) {
  1584. if (!isset($GLOBALS['table_data'])
  1585. || (isset($GLOBALS['table_data'])
  1586. && in_array($table, $GLOBALS['table_data']))
  1587. ) {
  1588. $sql_auto_increments_query .= ', AUTO_INCREMENT='
  1589. . $statement->entityOptions->has('AUTO_INCREMENT');
  1590. }
  1591. }
  1592. $sql_auto_increments_query .= ';' . $crlf;
  1593. $sql_auto_increments = $this->generateComment(
  1594. $crlf,
  1595. $sql_auto_increments,
  1596. __('AUTO_INCREMENT for dumped tables'),
  1597. __('AUTO_INCREMENT for table'),
  1598. $table_alias,
  1599. $compat
  1600. ) . $sql_auto_increments_query;
  1601. }
  1602. // Removing the `AUTO_INCREMENT` attribute from the `CREATE TABLE`
  1603. // too.
  1604. if (!empty($statement->entityOptions)
  1605. && (empty($GLOBALS['sql_if_not_exists'])
  1606. || empty($GLOBALS['sql_auto_increment']))
  1607. ) {
  1608. $statement->entityOptions->remove('AUTO_INCREMENT');
  1609. }
  1610. // Rebuilding the query.
  1611. $create_query = $statement->build();
  1612. }
  1613. $schema_create .= $create_query;
  1614. }
  1615. $GLOBALS['dbi']->freeResult($result);
  1616. // Restoring old mode.
  1617. Context::$MODE = $old_mode;
  1618. return $warning . $schema_create . ($add_semicolon ? ';' . $crlf : '');
  1619. } // end of the 'getTableDef()' function
  1620. /**
  1621. * Returns $table's comments, relations etc.
  1622. *
  1623. * @param string $db database name
  1624. * @param string $table table name
  1625. * @param string $crlf end of line sequence
  1626. * @param bool $do_relation whether to include relation comments
  1627. * @param bool $do_mime whether to include mime comments
  1628. * @param array $aliases Aliases of db/table/columns
  1629. *
  1630. * @return string resulting comments
  1631. */
  1632. private function _getTableComments(
  1633. $db,
  1634. $table,
  1635. $crlf,
  1636. $do_relation = false,
  1637. $do_mime = false,
  1638. array $aliases = array()
  1639. ) {
  1640. global $cfgRelation, $sql_backquotes;
  1641. $db_alias = $db;
  1642. $table_alias = $table;
  1643. $this->initAlias($aliases, $db_alias, $table_alias);
  1644. $schema_create = '';
  1645. // Check if we can use Relations
  1646. list($res_rel, $have_rel) = $this->relation->getRelationsAndStatus(
  1647. $do_relation && !empty($cfgRelation['relation']),
  1648. $db,
  1649. $table
  1650. );
  1651. if ($do_mime && $cfgRelation['mimework']) {
  1652. if (!($mime_map = Transformations::getMIME($db, $table, true))) {
  1653. unset($mime_map);
  1654. }
  1655. }
  1656. if (isset($mime_map) && count($mime_map) > 0) {
  1657. $schema_create .= $this->_possibleCRLF()
  1658. . $this->_exportComment()
  1659. . $this->_exportComment(
  1660. __('MIME TYPES FOR TABLE') . ' '
  1661. . Util::backquote($table, $sql_backquotes) . ':'
  1662. );
  1663. foreach ($mime_map as $mime_field => $mime) {
  1664. $schema_create .= $this->_exportComment(
  1665. ' '
  1666. . Util::backquote($mime_field, $sql_backquotes)
  1667. )
  1668. . $this->_exportComment(
  1669. ' '
  1670. . Util::backquote(
  1671. $mime['mimetype'],
  1672. $sql_backquotes
  1673. )
  1674. );
  1675. }
  1676. $schema_create .= $this->_exportComment();
  1677. }
  1678. if ($have_rel) {
  1679. $schema_create .= $this->_possibleCRLF()
  1680. . $this->_exportComment()
  1681. . $this->_exportComment(
  1682. __('RELATIONSHIPS FOR TABLE') . ' '
  1683. . Util::backquote($table_alias, $sql_backquotes)
  1684. . ':'
  1685. );
  1686. foreach ($res_rel as $rel_field => $rel) {
  1687. if ($rel_field != 'foreign_keys_data') {
  1688. $rel_field_alias = !empty(
  1689. $aliases[$db]['tables'][$table]['columns'][$rel_field]
  1690. ) ? $aliases[$db]['tables'][$table]['columns'][$rel_field]
  1691. : $rel_field;
  1692. $schema_create .= $this->_exportComment(
  1693. ' '
  1694. . Util::backquote(
  1695. $rel_field_alias,
  1696. $sql_backquotes
  1697. )
  1698. )
  1699. . $this->_exportComment(
  1700. ' '
  1701. . Util::backquote(
  1702. $rel['foreign_table'],
  1703. $sql_backquotes
  1704. )
  1705. . ' -> '
  1706. . Util::backquote(
  1707. $rel['foreign_field'],
  1708. $sql_backquotes
  1709. )
  1710. );
  1711. } else {
  1712. foreach ($rel as $one_key) {
  1713. foreach ($one_key['index_list'] as $index => $field) {
  1714. $rel_field_alias = !empty(
  1715. $aliases[$db]['tables'][$table]['columns'][$field]
  1716. ) ? $aliases[$db]['tables'][$table]['columns'][$field]
  1717. : $field;
  1718. $schema_create .= $this->_exportComment(
  1719. ' '
  1720. . Util::backquote(
  1721. $rel_field_alias,
  1722. $sql_backquotes
  1723. )
  1724. )
  1725. . $this->_exportComment(
  1726. ' '
  1727. . Util::backquote(
  1728. $one_key['ref_table_name'],
  1729. $sql_backquotes
  1730. )
  1731. . ' -> '
  1732. . Util::backquote(
  1733. $one_key['ref_index_list'][$index],
  1734. $sql_backquotes
  1735. )
  1736. );
  1737. }
  1738. }
  1739. }
  1740. }
  1741. $schema_create .= $this->_exportComment();
  1742. }
  1743. return $schema_create;
  1744. } // end of the '_getTableComments()' function
  1745. /**
  1746. * Outputs table's structure
  1747. *
  1748. * @param string $db database name
  1749. * @param string $table table name
  1750. * @param string $crlf the end of line sequence
  1751. * @param string $error_url the url to go back in case of error
  1752. * @param string $export_mode 'create_table','triggers','create_view',
  1753. * 'stand_in'
  1754. * @param string $export_type 'server', 'database', 'table'
  1755. * @param bool $relation whether to include relation comments
  1756. * @param bool $comments whether to include the pmadb-style column
  1757. * comments as comments in the structure; this is
  1758. * deprecated but the parameter is left here
  1759. * because export.php calls exportStructure()
  1760. * also for other export types which use this
  1761. * parameter
  1762. * @param bool $mime whether to include mime comments
  1763. * @param bool $dates whether to include creation/update/check dates
  1764. * @param array $aliases Aliases of db/table/columns
  1765. *
  1766. * @return bool Whether it succeeded
  1767. */
  1768. public function exportStructure(
  1769. $db,
  1770. $table,
  1771. $crlf,
  1772. $error_url,
  1773. $export_mode,
  1774. $export_type,
  1775. $relation = false,
  1776. $comments = false,
  1777. $mime = false,
  1778. $dates = false,
  1779. array $aliases = array()
  1780. ) {
  1781. $db_alias = $db;
  1782. $table_alias = $table;
  1783. $this->initAlias($aliases, $db_alias, $table_alias);
  1784. if (isset($GLOBALS['sql_compatibility'])) {
  1785. $compat = $GLOBALS['sql_compatibility'];
  1786. } else {
  1787. $compat = 'NONE';
  1788. }
  1789. $formatted_table_name = Util::backquoteCompat(
  1790. $table_alias,
  1791. $compat,
  1792. isset($GLOBALS['sql_backquotes'])
  1793. );
  1794. $dump = $this->_possibleCRLF()
  1795. . $this->_exportComment(str_repeat('-', 56))
  1796. . $this->_possibleCRLF()
  1797. . $this->_exportComment();
  1798. switch ($export_mode) {
  1799. case 'create_table':
  1800. $dump .= $this->_exportComment(
  1801. __('Table structure for table') . ' ' . $formatted_table_name
  1802. );
  1803. $dump .= $this->_exportComment();
  1804. $dump .= $this->getTableDef(
  1805. $db,
  1806. $table,
  1807. $crlf,
  1808. $error_url,
  1809. $dates,
  1810. true,
  1811. false,
  1812. true,
  1813. $aliases
  1814. );
  1815. $dump .= $this->_getTableComments(
  1816. $db,
  1817. $table,
  1818. $crlf,
  1819. $relation,
  1820. $mime,
  1821. $aliases
  1822. );
  1823. break;
  1824. case 'triggers':
  1825. $dump = '';
  1826. $delimiter = '$$';
  1827. $triggers = $GLOBALS['dbi']->getTriggers($db, $table, $delimiter);
  1828. if ($triggers) {
  1829. $dump .= $this->_possibleCRLF()
  1830. . $this->_exportComment()
  1831. . $this->_exportComment(
  1832. __('Triggers') . ' ' . $formatted_table_name
  1833. )
  1834. . $this->_exportComment();
  1835. $used_alias = false;
  1836. $trigger_query = '';
  1837. foreach ($triggers as $trigger) {
  1838. if (!empty($GLOBALS['sql_drop_table'])) {
  1839. $trigger_query .= $trigger['drop'] . ';' . $crlf;
  1840. }
  1841. $trigger_query .= 'DELIMITER ' . $delimiter . $crlf;
  1842. $trigger_query .= $this->replaceWithAliases(
  1843. $trigger['create'],
  1844. $aliases,
  1845. $db,
  1846. $table,
  1847. $flag
  1848. );
  1849. if ($flag) {
  1850. $used_alias = true;
  1851. }
  1852. $trigger_query .= 'DELIMITER ;' . $crlf;
  1853. }
  1854. // One warning per table.
  1855. if ($used_alias) {
  1856. $dump .= $this->_exportComment(
  1857. __('It appears your table uses triggers;')
  1858. )
  1859. . $this->_exportComment(
  1860. __('alias export may not work reliably in all cases.')
  1861. )
  1862. . $this->_exportComment();
  1863. }
  1864. $dump .= $trigger_query;
  1865. }
  1866. break;
  1867. case 'create_view':
  1868. if (empty($GLOBALS['sql_views_as_tables'])) {
  1869. $dump .= $this->_exportComment(
  1870. __('Structure for view')
  1871. . ' '
  1872. . $formatted_table_name
  1873. )
  1874. . $this->_exportComment();
  1875. // delete the stand-in table previously created (if any)
  1876. if ($export_type != 'table') {
  1877. $dump .= 'DROP TABLE IF EXISTS '
  1878. . Util::backquote($table_alias) . ';' . $crlf;
  1879. }
  1880. $dump .= $this->getTableDef(
  1881. $db,
  1882. $table,
  1883. $crlf,
  1884. $error_url,
  1885. $dates,
  1886. true,
  1887. true,
  1888. true,
  1889. $aliases
  1890. );
  1891. } else {
  1892. $dump .= $this->_exportComment(
  1893. sprintf(
  1894. __('Structure for view %s exported as a table'),
  1895. $formatted_table_name
  1896. )
  1897. )
  1898. . $this->_exportComment();
  1899. // delete the stand-in table previously created (if any)
  1900. if ($export_type != 'table') {
  1901. $dump .= 'DROP TABLE IF EXISTS '
  1902. . Util::backquote($table_alias) . ';' . $crlf;
  1903. }
  1904. $dump .= $this->_getTableDefForView(
  1905. $db,
  1906. $table,
  1907. $crlf,
  1908. true,
  1909. $aliases
  1910. );
  1911. }
  1912. break;
  1913. case 'stand_in':
  1914. $dump .= $this->_exportComment(
  1915. __('Stand-in structure for view') . ' ' . $formatted_table_name
  1916. )
  1917. . $this->_exportComment(
  1918. __('(See below for the actual view)')
  1919. )
  1920. . $this->_exportComment();
  1921. // export a stand-in definition to resolve view dependencies
  1922. $dump .= $this->getTableDefStandIn($db, $table, $crlf, $aliases);
  1923. } // end switch
  1924. // this one is built by getTableDef() to use in table copy/move
  1925. // but not in the case of export
  1926. unset($GLOBALS['sql_constraints_query']);
  1927. return Export::outputHandler($dump);
  1928. }
  1929. /**
  1930. * Outputs the content of a table in SQL format
  1931. *
  1932. * @param string $db database name
  1933. * @param string $table table name
  1934. * @param string $crlf the end of line sequence
  1935. * @param string $error_url the url to go back in case of error
  1936. * @param string $sql_query SQL query for obtaining data
  1937. * @param array $aliases Aliases of db/table/columns
  1938. *
  1939. * @return bool Whether it succeeded
  1940. */
  1941. public function exportData(
  1942. $db,
  1943. $table,
  1944. $crlf,
  1945. $error_url,
  1946. $sql_query,
  1947. array $aliases = array()
  1948. ) {
  1949. global $current_row, $sql_backquotes;
  1950. // Do not export data for merge tables
  1951. if ($GLOBALS['dbi']->getTable($db, $table)->isMerge()) {
  1952. return true;
  1953. }
  1954. $db_alias = $db;
  1955. $table_alias = $table;
  1956. $this->initAlias($aliases, $db_alias, $table_alias);
  1957. if (isset($GLOBALS['sql_compatibility'])) {
  1958. $compat = $GLOBALS['sql_compatibility'];
  1959. } else {
  1960. $compat = 'NONE';
  1961. }
  1962. $formatted_table_name = Util::backquoteCompat(
  1963. $table_alias,
  1964. $compat,
  1965. $sql_backquotes
  1966. );
  1967. // Do not export data for a VIEW, unless asked to export the view as a table
  1968. // (For a VIEW, this is called only when exporting a single VIEW)
  1969. if ($GLOBALS['dbi']->getTable($db, $table)->isView()
  1970. && empty($GLOBALS['sql_views_as_tables'])
  1971. ) {
  1972. $head = $this->_possibleCRLF()
  1973. . $this->_exportComment()
  1974. . $this->_exportComment('VIEW ' . ' ' . $formatted_table_name)
  1975. . $this->_exportComment(__('Data:') . ' ' . __('None'))
  1976. . $this->_exportComment()
  1977. . $this->_possibleCRLF();
  1978. return Export::outputHandler($head);
  1979. }
  1980. $result = $GLOBALS['dbi']->tryQuery(
  1981. $sql_query,
  1982. DatabaseInterface::CONNECT_USER,
  1983. DatabaseInterface::QUERY_UNBUFFERED
  1984. );
  1985. // a possible error: the table has crashed
  1986. $tmp_error = $GLOBALS['dbi']->getError();
  1987. if ($tmp_error) {
  1988. $message = sprintf(__('Error reading data for table %s:'), "$db.$table");
  1989. $message .= ' ' . $tmp_error;
  1990. if (! defined('TESTSUITE')) {
  1991. trigger_error($message, E_USER_ERROR);
  1992. }
  1993. return Export::outputHandler(
  1994. $this->_exportComment($message)
  1995. );
  1996. }
  1997. if ($result == false) {
  1998. $GLOBALS['dbi']->freeResult($result);
  1999. return true;
  2000. }
  2001. $fields_cnt = $GLOBALS['dbi']->numFields($result);
  2002. // Get field information
  2003. $fields_meta = $GLOBALS['dbi']->getFieldsMeta($result);
  2004. $field_flags = array();
  2005. for ($j = 0; $j < $fields_cnt; $j++) {
  2006. $field_flags[$j] = $GLOBALS['dbi']->fieldFlags($result, $j);
  2007. }
  2008. $field_set = array();
  2009. for ($j = 0; $j < $fields_cnt; $j++) {
  2010. $col_as = $fields_meta[$j]->name;
  2011. if (!empty($aliases[$db]['tables'][$table]['columns'][$col_as])) {
  2012. $col_as = $aliases[$db]['tables'][$table]['columns'][$col_as];
  2013. }
  2014. $field_set[$j] = Util::backquoteCompat(
  2015. $col_as,
  2016. $compat,
  2017. $sql_backquotes
  2018. );
  2019. }
  2020. if (isset($GLOBALS['sql_type'])
  2021. && $GLOBALS['sql_type'] == 'UPDATE'
  2022. ) {
  2023. // update
  2024. $schema_insert = 'UPDATE ';
  2025. if (isset($GLOBALS['sql_ignore'])) {
  2026. $schema_insert .= 'IGNORE ';
  2027. }
  2028. // avoid EOL blank
  2029. $schema_insert .= Util::backquoteCompat(
  2030. $table_alias,
  2031. $compat,
  2032. $sql_backquotes
  2033. ) . ' SET';
  2034. } else {
  2035. // insert or replace
  2036. if (isset($GLOBALS['sql_type'])
  2037. && $GLOBALS['sql_type'] == 'REPLACE'
  2038. ) {
  2039. $sql_command = 'REPLACE';
  2040. } else {
  2041. $sql_command = 'INSERT';
  2042. }
  2043. // delayed inserts?
  2044. if (isset($GLOBALS['sql_delayed'])) {
  2045. $insert_delayed = ' DELAYED';
  2046. } else {
  2047. $insert_delayed = '';
  2048. }
  2049. // insert ignore?
  2050. if (isset($GLOBALS['sql_type'])
  2051. && $GLOBALS['sql_type'] == 'INSERT'
  2052. && isset($GLOBALS['sql_ignore'])
  2053. ) {
  2054. $insert_delayed .= ' IGNORE';
  2055. }
  2056. //truncate table before insert
  2057. if (isset($GLOBALS['sql_truncate'])
  2058. && $GLOBALS['sql_truncate']
  2059. && $sql_command == 'INSERT'
  2060. ) {
  2061. $truncate = 'TRUNCATE TABLE '
  2062. . Util::backquoteCompat(
  2063. $table_alias,
  2064. $compat,
  2065. $sql_backquotes
  2066. ) . ";";
  2067. $truncatehead = $this->_possibleCRLF()
  2068. . $this->_exportComment()
  2069. . $this->_exportComment(
  2070. __('Truncate table before insert') . ' '
  2071. . $formatted_table_name
  2072. )
  2073. . $this->_exportComment()
  2074. . $crlf;
  2075. Export::outputHandler($truncatehead);
  2076. Export::outputHandler($truncate);
  2077. }
  2078. // scheme for inserting fields
  2079. if ($GLOBALS['sql_insert_syntax'] == 'complete'
  2080. || $GLOBALS['sql_insert_syntax'] == 'both'
  2081. ) {
  2082. $fields = implode(', ', $field_set);
  2083. $schema_insert = $sql_command . $insert_delayed . ' INTO '
  2084. . Util::backquoteCompat(
  2085. $table_alias,
  2086. $compat,
  2087. $sql_backquotes
  2088. )
  2089. // avoid EOL blank
  2090. . ' (' . $fields . ') VALUES';
  2091. } else {
  2092. $schema_insert = $sql_command . $insert_delayed . ' INTO '
  2093. . Util::backquoteCompat(
  2094. $table_alias,
  2095. $compat,
  2096. $sql_backquotes
  2097. )
  2098. . ' VALUES';
  2099. }
  2100. }
  2101. //\x08\\x09, not required
  2102. $current_row = 0;
  2103. $query_size = 0;
  2104. if (($GLOBALS['sql_insert_syntax'] == 'extended'
  2105. || $GLOBALS['sql_insert_syntax'] == 'both')
  2106. && (!isset($GLOBALS['sql_type'])
  2107. || $GLOBALS['sql_type'] != 'UPDATE')
  2108. ) {
  2109. $separator = ',';
  2110. $schema_insert .= $crlf;
  2111. } else {
  2112. $separator = ';';
  2113. }
  2114. while ($row = $GLOBALS['dbi']->fetchRow($result)) {
  2115. if ($current_row == 0) {
  2116. $head = $this->_possibleCRLF()
  2117. . $this->_exportComment()
  2118. . $this->_exportComment(
  2119. __('Dumping data for table') . ' '
  2120. . $formatted_table_name
  2121. )
  2122. . $this->_exportComment()
  2123. . $crlf;
  2124. if (!Export::outputHandler($head)) {
  2125. return false;
  2126. }
  2127. }
  2128. // We need to SET IDENTITY_INSERT ON for MSSQL
  2129. if (isset($GLOBALS['sql_compatibility'])
  2130. && $GLOBALS['sql_compatibility'] == 'MSSQL'
  2131. && $current_row == 0
  2132. ) {
  2133. if (!Export::outputHandler(
  2134. 'SET IDENTITY_INSERT '
  2135. . Util::backquoteCompat(
  2136. $table_alias,
  2137. $compat,
  2138. $sql_backquotes
  2139. )
  2140. . ' ON ;' . $crlf
  2141. )
  2142. ) {
  2143. return false;
  2144. }
  2145. }
  2146. $current_row++;
  2147. $values = array();
  2148. for ($j = 0; $j < $fields_cnt; $j++) {
  2149. // NULL
  2150. if (!isset($row[$j]) || is_null($row[$j])) {
  2151. $values[] = 'NULL';
  2152. } elseif ($fields_meta[$j]->numeric
  2153. && $fields_meta[$j]->type != 'timestamp'
  2154. && !$fields_meta[$j]->blob
  2155. ) {
  2156. // a number
  2157. // timestamp is numeric on some MySQL 4.1, BLOBs are
  2158. // sometimes numeric
  2159. $values[] = $row[$j];
  2160. } elseif (stristr($field_flags[$j], 'BINARY') !== false
  2161. && isset($GLOBALS['sql_hex_for_binary'])
  2162. ) {
  2163. // a true BLOB
  2164. // - mysqldump only generates hex data when the --hex-blob
  2165. // option is used, for fields having the binary attribute
  2166. // no hex is generated
  2167. // - a TEXT field returns type blob but a real blob
  2168. // returns also the 'binary' flag
  2169. // empty blobs need to be different, but '0' is also empty
  2170. // :-(
  2171. if (empty($row[$j]) && $row[$j] != '0') {
  2172. $values[] = '\'\'';
  2173. } else {
  2174. $values[] = '0x' . bin2hex($row[$j]);
  2175. }
  2176. } elseif ($fields_meta[$j]->type == 'bit') {
  2177. // detection of 'bit' works only on mysqli extension
  2178. $values[] = "b'" . $GLOBALS['dbi']->escapeString(
  2179. Util::printableBitValue(
  2180. $row[$j],
  2181. $fields_meta[$j]->length
  2182. )
  2183. )
  2184. . "'";
  2185. } elseif ($fields_meta[$j]->type === 'geometry') {
  2186. // export GIS types as hex
  2187. $values[] = '0x' . bin2hex($row[$j]);
  2188. } elseif (!empty($GLOBALS['exporting_metadata'])
  2189. && $row[$j] == '@LAST_PAGE'
  2190. ) {
  2191. $values[] = '@LAST_PAGE';
  2192. } else {
  2193. // something else -> treat as a string
  2194. $values[] = '\''
  2195. . $GLOBALS['dbi']->escapeString($row[$j])
  2196. . '\'';
  2197. } // end if
  2198. } // end for
  2199. // should we make update?
  2200. if (isset($GLOBALS['sql_type'])
  2201. && $GLOBALS['sql_type'] == 'UPDATE'
  2202. ) {
  2203. $insert_line = $schema_insert;
  2204. for ($i = 0; $i < $fields_cnt; $i++) {
  2205. if (0 == $i) {
  2206. $insert_line .= ' ';
  2207. }
  2208. if ($i > 0) {
  2209. // avoid EOL blank
  2210. $insert_line .= ',';
  2211. }
  2212. $insert_line .= $field_set[$i] . ' = ' . $values[$i];
  2213. }
  2214. list($tmp_unique_condition, $tmp_clause_is_unique)
  2215. = Util::getUniqueCondition(
  2216. $result, // handle
  2217. $fields_cnt, // fields_cnt
  2218. $fields_meta, // fields_meta
  2219. $row, // row
  2220. false, // force_unique
  2221. false, // restrict_to_table
  2222. null // analyzed_sql_results
  2223. );
  2224. $insert_line .= ' WHERE ' . $tmp_unique_condition;
  2225. unset($tmp_unique_condition, $tmp_clause_is_unique);
  2226. } else {
  2227. // Extended inserts case
  2228. if ($GLOBALS['sql_insert_syntax'] == 'extended'
  2229. || $GLOBALS['sql_insert_syntax'] == 'both'
  2230. ) {
  2231. if ($current_row == 1) {
  2232. $insert_line = $schema_insert . '('
  2233. . implode(', ', $values) . ')';
  2234. } else {
  2235. $insert_line = '(' . implode(', ', $values) . ')';
  2236. $insertLineSize = mb_strlen($insert_line);
  2237. $sql_max_size = $GLOBALS['sql_max_query_size'];
  2238. if (isset($sql_max_size)
  2239. && $sql_max_size > 0
  2240. && $query_size + $insertLineSize > $sql_max_size
  2241. ) {
  2242. if (!Export::outputHandler(';' . $crlf)) {
  2243. return false;
  2244. }
  2245. $query_size = 0;
  2246. $current_row = 1;
  2247. $insert_line = $schema_insert . $insert_line;
  2248. }
  2249. }
  2250. $query_size += mb_strlen($insert_line);
  2251. // Other inserts case
  2252. } else {
  2253. $insert_line = $schema_insert
  2254. . '(' . implode(', ', $values) . ')';
  2255. }
  2256. }
  2257. unset($values);
  2258. if (!Export::outputHandler(
  2259. ($current_row == 1 ? '' : $separator . $crlf)
  2260. . $insert_line
  2261. )
  2262. ) {
  2263. return false;
  2264. }
  2265. } // end while
  2266. if ($current_row > 0) {
  2267. if (!Export::outputHandler(';' . $crlf)) {
  2268. return false;
  2269. }
  2270. }
  2271. // We need to SET IDENTITY_INSERT OFF for MSSQL
  2272. if (isset($GLOBALS['sql_compatibility'])
  2273. && $GLOBALS['sql_compatibility'] == 'MSSQL'
  2274. && $current_row > 0
  2275. ) {
  2276. $outputSucceeded = Export::outputHandler(
  2277. $crlf . 'SET IDENTITY_INSERT '
  2278. . Util::backquoteCompat(
  2279. $table_alias,
  2280. $compat,
  2281. $sql_backquotes
  2282. )
  2283. . ' OFF;' . $crlf
  2284. );
  2285. if (!$outputSucceeded) {
  2286. return false;
  2287. }
  2288. }
  2289. $GLOBALS['dbi']->freeResult($result);
  2290. return true;
  2291. } // end of the 'exportData()' function
  2292. /**
  2293. * Make a create table statement compatible with MSSQL
  2294. *
  2295. * @param string $create_query MySQL create table statement
  2296. *
  2297. * @return string MSSQL compatible create table statement
  2298. */
  2299. private function _makeCreateTableMSSQLCompatible($create_query)
  2300. {
  2301. // In MSSQL
  2302. // 1. No 'IF NOT EXISTS' in CREATE TABLE
  2303. // 2. DATE field doesn't exists, we will use DATETIME instead
  2304. // 3. UNSIGNED attribute doesn't exist
  2305. // 4. No length on INT, TINYINT, SMALLINT, BIGINT and no precision on
  2306. // FLOAT fields
  2307. // 5. No KEY and INDEX inside CREATE TABLE
  2308. // 6. DOUBLE field doesn't exists, we will use FLOAT instead
  2309. $create_query = preg_replace(
  2310. "/^CREATE TABLE IF NOT EXISTS/",
  2311. 'CREATE TABLE',
  2312. $create_query
  2313. );
  2314. // first we need to replace all lines ended with '" DATE ...,\n'
  2315. // last preg_replace preserve us from situation with date text
  2316. // inside DEFAULT field value
  2317. $create_query = preg_replace(
  2318. "/\" date DEFAULT NULL(,)?\n/",
  2319. '" datetime DEFAULT NULL$1' . "\n",
  2320. $create_query
  2321. );
  2322. $create_query = preg_replace(
  2323. "/\" date NOT NULL(,)?\n/",
  2324. '" datetime NOT NULL$1' . "\n",
  2325. $create_query
  2326. );
  2327. $create_query = preg_replace(
  2328. '/" date NOT NULL DEFAULT \'([^\'])/',
  2329. '" datetime NOT NULL DEFAULT \'$1',
  2330. $create_query
  2331. );
  2332. // next we need to replace all lines ended with ') UNSIGNED ...,'
  2333. // last preg_replace preserve us from situation with unsigned text
  2334. // inside DEFAULT field value
  2335. $create_query = preg_replace(
  2336. "/\) unsigned NOT NULL(,)?\n/",
  2337. ') NOT NULL$1' . "\n",
  2338. $create_query
  2339. );
  2340. $create_query = preg_replace(
  2341. "/\) unsigned DEFAULT NULL(,)?\n/",
  2342. ') DEFAULT NULL$1' . "\n",
  2343. $create_query
  2344. );
  2345. $create_query = preg_replace(
  2346. '/\) unsigned NOT NULL DEFAULT \'([^\'])/',
  2347. ') NOT NULL DEFAULT \'$1',
  2348. $create_query
  2349. );
  2350. // we need to replace all lines ended with
  2351. // '" INT|TINYINT([0-9]{1,}) ...,' last preg_replace preserve us
  2352. // from situation with int([0-9]{1,}) text inside DEFAULT field
  2353. // value
  2354. $create_query = preg_replace(
  2355. '/" (int|tinyint|smallint|bigint)\([0-9]+\) DEFAULT NULL(,)?\n/',
  2356. '" $1 DEFAULT NULL$2' . "\n",
  2357. $create_query
  2358. );
  2359. $create_query = preg_replace(
  2360. '/" (int|tinyint|smallint|bigint)\([0-9]+\) NOT NULL(,)?\n/',
  2361. '" $1 NOT NULL$2' . "\n",
  2362. $create_query
  2363. );
  2364. $create_query = preg_replace(
  2365. '/" (int|tinyint|smallint|bigint)\([0-9]+\) NOT NULL DEFAULT \'([^\'])/',
  2366. '" $1 NOT NULL DEFAULT \'$2',
  2367. $create_query
  2368. );
  2369. // we need to replace all lines ended with
  2370. // '" FLOAT|DOUBLE([0-9,]{1,}) ...,'
  2371. // last preg_replace preserve us from situation with
  2372. // float([0-9,]{1,}) text inside DEFAULT field value
  2373. $create_query = preg_replace(
  2374. '/" (float|double)(\([0-9]+,[0-9,]+\))? DEFAULT NULL(,)?\n/',
  2375. '" float DEFAULT NULL$3' . "\n",
  2376. $create_query
  2377. );
  2378. $create_query = preg_replace(
  2379. '/" (float|double)(\([0-9,]+,[0-9,]+\))? NOT NULL(,)?\n/',
  2380. '" float NOT NULL$3' . "\n",
  2381. $create_query
  2382. );
  2383. $create_query = preg_replace(
  2384. '/" (float|double)(\([0-9,]+,[0-9,]+\))? NOT NULL DEFAULT \'([^\'])/',
  2385. '" float NOT NULL DEFAULT \'$3',
  2386. $create_query
  2387. );
  2388. // @todo remove indexes from CREATE TABLE
  2389. return $create_query;
  2390. }
  2391. /**
  2392. * replaces db/table/column names with their aliases
  2393. *
  2394. * @param string $sql_query SQL query in which aliases are to be substituted
  2395. * @param array $aliases Alias information for db/table/column
  2396. * @param string $db the database name
  2397. * @param string $table the tablename
  2398. * @param string &$flag the flag denoting whether any replacement was done
  2399. *
  2400. * @return string query replaced with aliases
  2401. */
  2402. public function replaceWithAliases(
  2403. $sql_query,
  2404. array $aliases,
  2405. $db,
  2406. $table = '',
  2407. &$flag = null
  2408. ) {
  2409. $flag = false;
  2410. /**
  2411. * The parser of this query.
  2412. *
  2413. * @var Parser $parser
  2414. */
  2415. $parser = new Parser($sql_query);
  2416. if (empty($parser->statements[0])) {
  2417. return $sql_query;
  2418. }
  2419. /**
  2420. * The statement that represents the query.
  2421. *
  2422. * @var \PhpMyAdmin\SqlParser\Statements\CreateStatement $statement
  2423. */
  2424. $statement = $parser->statements[0];
  2425. /**
  2426. * Old database name.
  2427. *
  2428. * @var string $old_database
  2429. */
  2430. $old_database = $db;
  2431. // Replacing aliases in `CREATE TABLE` statement.
  2432. if ($statement->options->has('TABLE')) {
  2433. // Extracting the name of the old database and table from the
  2434. // statement to make sure the parameters are corect.
  2435. if (!empty($statement->name->database)) {
  2436. $old_database = $statement->name->database;
  2437. }
  2438. /**
  2439. * Old table name.
  2440. *
  2441. * @var string $old_table
  2442. */
  2443. $old_table = $statement->name->table;
  2444. // Finding the aliased database name.
  2445. // The database might be empty so we have to add a few checks.
  2446. $new_database = null;
  2447. if (!empty($statement->name->database)) {
  2448. $new_database = $statement->name->database;
  2449. if (!empty($aliases[$old_database]['alias'])) {
  2450. $new_database = $aliases[$old_database]['alias'];
  2451. }
  2452. }
  2453. // Finding the aliases table name.
  2454. $new_table = $old_table;
  2455. if (!empty($aliases[$old_database]['tables'][$old_table]['alias'])) {
  2456. $new_table = $aliases[$old_database]['tables'][$old_table]['alias'];
  2457. }
  2458. // Replacing new values.
  2459. if (($statement->name->database !== $new_database)
  2460. || ($statement->name->table !== $new_table)
  2461. ) {
  2462. $statement->name->database = $new_database;
  2463. $statement->name->table = $new_table;
  2464. $statement->name->expr = null; // Force rebuild.
  2465. $flag = true;
  2466. }
  2467. foreach ($statement->fields as $field) {
  2468. // Column name.
  2469. if (!empty($field->type)) {
  2470. if (!empty($aliases[$old_database]['tables'][$old_table]['columns'][$field->name])) {
  2471. $field->name = $aliases[$old_database]['tables']
  2472. [$old_table]['columns'][$field->name];
  2473. $flag = true;
  2474. }
  2475. }
  2476. // Key's columns.
  2477. if (!empty($field->key)) {
  2478. foreach ($field->key->columns as $key => $column) {
  2479. if (!empty($aliases[$old_database]['tables'][$old_table]['columns'][$column['name']])) {
  2480. $field->key->columns[$key]['name'] = $aliases[$old_database]
  2481. ['tables'][$old_table]['columns'][$column['name']];
  2482. $flag = true;
  2483. }
  2484. }
  2485. }
  2486. // References.
  2487. if (!empty($field->references)) {
  2488. $ref_table = $field->references->table->table;
  2489. // Replacing table.
  2490. if (!empty($aliases[$old_database]['tables'][$ref_table]['alias'])) {
  2491. $field->references->table->table
  2492. = $aliases[$old_database]['tables'][$ref_table]['alias'];
  2493. $field->references->table->expr = null;
  2494. $flag = true;
  2495. }
  2496. // Replacing column names.
  2497. foreach ($field->references->columns as $key => $column) {
  2498. if (!empty($aliases[$old_database]['tables'][$ref_table]['columns'][$column])) {
  2499. $field->references->columns[$key]
  2500. = $aliases[$old_database]['tables'][$ref_table]['columns'][$column];
  2501. $flag = true;
  2502. }
  2503. }
  2504. }
  2505. }
  2506. } elseif ($statement->options->has('TRIGGER')) {
  2507. // Extracting the name of the old database and table from the
  2508. // statement to make sure the parameters are corect.
  2509. if (!empty($statement->table->database)) {
  2510. $old_database = $statement->table->database;
  2511. }
  2512. /**
  2513. * Old table name.
  2514. *
  2515. * @var string $old_table
  2516. */
  2517. $old_table = $statement->table->table;
  2518. if (!empty($aliases[$old_database]['tables'][$old_table]['alias'])) {
  2519. $statement->table->table
  2520. = $aliases[$old_database]['tables'][$old_table]['alias'];
  2521. $statement->table->expr = null; // Force rebuild.
  2522. $flag = true;
  2523. }
  2524. }
  2525. if (($statement->options->has('TRIGGER'))
  2526. || ($statement->options->has('PROCEDURE'))
  2527. || ($statement->options->has('FUNCTION'))
  2528. || ($statement->options->has('VIEW'))
  2529. ) {
  2530. // Repalcing the body.
  2531. for ($i = 0, $count = count($statement->body); $i < $count; ++$i) {
  2532. /**
  2533. * Token parsed at this moment.
  2534. *
  2535. * @var Token $token
  2536. */
  2537. $token = $statement->body[$i];
  2538. // Replacing only symbols (that are not variables) and unknown
  2539. // identifiers.
  2540. if ((($token->type === Token::TYPE_SYMBOL)
  2541. && (!($token->flags & Token::FLAG_SYMBOL_VARIABLE)))
  2542. || ((($token->type === Token::TYPE_KEYWORD)
  2543. && (!($token->flags & Token::FLAG_KEYWORD_RESERVED)))
  2544. || ($token->type === Token::TYPE_NONE))
  2545. ) {
  2546. $alias = $this->getAlias($aliases, $token->value);
  2547. if (!empty($alias)) {
  2548. // Replacing the token.
  2549. $token->token = Context::escape($alias);
  2550. $flag = true;
  2551. }
  2552. }
  2553. }
  2554. }
  2555. return $statement->build();
  2556. }
  2557. /**
  2558. * Generate comment
  2559. *
  2560. * @param string $crlf Carriage return character
  2561. * @param string $sql_statement SQL statement
  2562. * @param string $comment1 Comment for dumped table
  2563. * @param string $comment2 Comment for current table
  2564. * @param string $table_alias Table alias
  2565. * @param string $compat Compatibility mode
  2566. *
  2567. * @return string
  2568. */
  2569. protected function generateComment(
  2570. $crlf,
  2571. $sql_statement,
  2572. $comment1,
  2573. $comment2,
  2574. $table_alias,
  2575. $compat
  2576. ) {
  2577. if (!isset($sql_statement)) {
  2578. if (isset($GLOBALS['no_constraints_comments'])) {
  2579. $sql_statement = '';
  2580. } else {
  2581. $sql_statement = $crlf
  2582. . $this->_exportComment()
  2583. . $this->_exportComment($comment1)
  2584. . $this->_exportComment();
  2585. }
  2586. }
  2587. // comments for current table
  2588. if (!isset($GLOBALS['no_constraints_comments'])) {
  2589. $sql_statement .= $crlf
  2590. . $this->_exportComment()
  2591. . $this->_exportComment(
  2592. $comment2 . ' ' . Util::backquoteCompat(
  2593. $table_alias,
  2594. $compat,
  2595. isset($GLOBALS['sql_backquotes'])
  2596. )
  2597. )
  2598. . $this->_exportComment();
  2599. }
  2600. return $sql_statement;
  2601. }
  2602. }