ExportSql.php 103 KB

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