ExportSql.php 101 KB

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