Creating FEDERATED tables with a stored procedure

Developed In: SQL — Contributed by: Roland Bouman

MySQL Procedure p_create_federated_table Purpose: create a MySQL Federated table Author: Roland Bouman http://rpbouman.blogspot.com/

A Federated table is a local table definition that uses the FEDERATED storage engine. The local FEDERATED table is only a definition. Data is not fysically stored in the FEDERATED table. Rather, the FEDERATED table has a peephole to the other server's table.

Creating a federated table is tedious, because in the local database, you need to define a table with exactly the same structure as the remote table. That includes the index definitions, because these are used by the local optimizer to create a query plan (although the statistics for the index will not be locally available)

This procedure creates automatically creates the federated table. It does so by reading from the information_schema on the remote host.

Sample1: call p_create_federated_table( '192.168.1.100' , 3306 , 'my_remote_username' , 'my_remote_password' , 'my_remote_schema' , 'my_remote_table' , 'my_local_schema' , 'my_local_table' );

This sample will create a FEDERATED table named 'my_local_table' in the schema 'my_local_schema'. The created table will point to the table 'my_remote_table' in the schema 'my_remote_schema' on the remote MySQL Server on the host 192.168.0.100 that is listening on port 3306

Sample2:

call p_create_federated_table( 'myhost.mydomain.org' , NULL , NULL , NULL , 'my_schema' , 'my_table' , NULL , NULL );

This sample will create a FEDERATED table with the same name as the specified remote table, and in the local schema with the same name as the specified remote schema. The remote mysql server is listening on the default mysql port (usually 3306) on the host myhost.mydomain.org'.

Limitations: - You need execute privilege for the procedure - The remote user must have privileges to access the remote table - The local user must have privileges to create the local table - The remote host must be a MySQL 5.0 or higher server (information_schema) - The local user must have the create/drop temp table privilege in the database wherein the procedure resides (to be fixed later). - generic security risk of FEDERATED: the connectstring can be displayed -- with a SHOW CREATE TABLE or SHOW TABLE STATUS command, or from the local information_schema.TABLES system view. - general: see http://dev.mysql.com/doc/refman/5.1/en/federated-limitations.html

more info on the FEDERATED storage engine:

http://dev.mysql.com/doc/refman/5.1/en/federated-storage-engine.html http://www.oreillynet.com/pub/a/databases/2006/08/10/mysql-federated-tables.html


Roland Bouman
SQL
  1. delimiter $$
  2.  
  3. DROP procedure IF EXISTS
  4. p_create_federated_table
  5. $$
  6. CREATE procedure
  7. p_create_federated_table
  8.  
  9. (
  10. -- the ip address or name of the remote mysql server host (if NULL, 'localhost')
  11. p_remote_host varchar(32)
  12. -- the port where the remote mysql server is listening (if NULL, 3306)
  13. , p_remote_port int UNSIGNED
  14. -- the user on the remote server that accesses the table (if NULL, 'root')
  15. , p_remote_user varchar(16) --
  16. -- the password for the remote user (if NULL, omitted)
  17. , p_remote_password varchar(32)
  18. -- the schema in which the remote table resides
  19. , p_remote_schema varchar(64)
  20. -- the name of the remote table
  21. , p_remote_table varchar(64)
  22. -- the local schema to create the local FEDERATED table (if NULL, p_remote_schema)
  23. , p_local_schema varchar(64)
  24. -- the name of the local FEDERATED table (if NULL, p_remote_table)
  25. , p_local_table varchar(64)
  26. )
  27. LANGUAGE SQL
  28. NOT DETERMINISTIC
  29. MODIFIES SQL DATA
  30. SQL SECURITY INVOKER
  31. COMMENT 'Creates a FEDERATED table.'
  32. /*
  33.   Changelog
  34.  
  35.   WHEN? WHO? WHAT?
  36.   -----------------------------------------------------------
  37.   2008-03-12 RPB Fixed speling error: statment -> statement
  38.  
  39.   2007-02-20 RPB added handler to ignore warning 1366. Not sure why this appears.
  40.   lowered the group_concat_max_len (bug #23856)
  41.   removed the ORDER BY clauses from the GROUP_CONCATs on COLUMN_TYPE (bug #23856)
  42.   added output so we can see what's taking so long
  43.   added changelog
  44.   2006-11-20 RPB Created Initial version
  45. */
  46. begin
  47. -- size used for the GROUP_CONCAT buffer if current is lower
  48. -- please see http://bugs.mysql.com/bug.php?id=23856
  49. declare v_group_concat_max_len smallint
  50. DEFAULT 16384;
  51. -- stores the original size of the GROUP_CONCAT buffer to restore it
  52. declare v_old_group_concat_max_len int UNSIGNED
  53. DEFAULT @@group_concat_max_len;
  54. -- stores the original sql_mode
  55. declare v_old_sql_mode varchar(255)
  56. DEFAULT @@sql_mode;
  57. -- Used to drop temporary tables
  58. declare v_drop_table_name varchar(64);
  59.  
  60. -- set the GROUP_CONCAT buffer sufficiently large
  61. SET @@group_concat_max_len := greatest(
  62. v_group_concat_max_len
  63. , v_old_group_concat_max_len
  64. );
  65. -- set the sql_mode to default to prevent invalid column defaults
  66. SET @@sql_mode := '';
  67.  
  68. -- The following block contains all the meat
  69. -- We use a separate block to allow for proper error handling.
  70. -- All errors that might be expected are handled inside this block
  71. -- This should guarantee that the outer block is always completed.
  72. -- That is necessary, beause we need to do a little cleaning up
  73. -- before exiting the procedure.
  74. begin
  75. -- Used for defaulting the specified host
  76. declare v_remote_host varchar(32)
  77. DEFAULT coalesce(p_remote_host,'localhost');
  78. -- Used for defaulting the specified user
  79. declare v_remote_user varchar(16)
  80. DEFAULT coalesce(p_remote_user,'root');
  81. -- Used for defaulting the local schema
  82. declare v_local_schema varchar(64)
  83. DEFAULT coalesce(p_local_schema,p_remote_schema);
  84. -- Used for defaulting the local schema
  85. declare v_local_table varchar(64)
  86. DEFAULT coalesce(p_local_table,p_remote_table);
  87. -- Holds the connectstring prefix for FEDERATED tables
  88. declare v_connectstring varchar(255)
  89. DEFAULT concat(
  90. 'mysql://'
  91. , v_remote_user
  92. , IF(p_remote_password IS NULL
  93. , ''
  94. , concat(':',p_remote_password)
  95. )
  96. , '@'
  97. , v_remote_host
  98. , IF(p_remote_port IS NULL
  99. , ''
  100. , concat(':',p_remote_port)
  101. )
  102. , '/'
  103. );
  104.  
  105. -- Various conditions we might encounter
  106. -- We rename them just for clarity
  107. declare TABLE_EXISTS_ERROR condition FOR 1050;
  108. declare UNKNOWN_COLUMN_ERROR condition FOR 1054;
  109. declare SYNTAX_ERROR condition FOR 1064;
  110. declare GROUP_CONCAT_TRUNCATION_ERROR condition FOR 1260;
  111. declare INCORRECT_VALUE_ERROR condition FOR 1366;
  112. declare TRUNCATION_ERROR condition FOR 1406;
  113. declare FEDERATION_ERROR condition FOR 1429;
  114.  
  115. -- Various handlers. These will execute if one of the conditions occur.
  116. -- They all show a friendly error message and the exit the inner block.
  117. -- Execution is resumed at the clean up code, just before
  118. -- the end of the proceudre
  119. declare exit handler FOR FEDERATION_ERROR
  120. SELECT 'Federation error' error_type
  121. , 'Check the connectstring.' error_message
  122. , v_connectstring connectstring
  123. ;
  124. declare exit handler FOR GROUP_CONCAT_TRUNCATION_ERROR
  125. SELECT 'GROUP_CONCAT Truncation' error_type
  126. , 'Increase GROUP_CONCAT_MAX_LEN.' error_message
  127. , @@group_concat_max_len group_concat_max_len
  128. ;
  129. /*
  130.   declare exit handler for SYNTAX_ERROR
  131.   select 'Syntax Error' error_type
  132.   , 'Check this statement.' error_message
  133.   , @create_table_statement statement
  134.   ;
  135. */
  136. declare exit handler FOR TABLE_EXISTS_ERROR
  137. SELECT 'Table Exists' error_type
  138. , 'Drop the table first.' error_message
  139. , concat(v_local_schema,'.',v_local_table) table_identifier
  140. ;
  141. declare exit handler FOR UNKNOWN_COLUMN_ERROR
  142. SELECT 'Unknown Column' error_type
  143. , 'Unexpected error, check the statement.' error_message
  144. , @create_table_statement table_identifier
  145. ;
  146. declare exit handler FOR NOT FOUND
  147. SELECT 'No such Table' error_type
  148. , 'The requested table was not found on the remote host.' error_message
  149. , concat(p_remote_schema,'.',p_remote_table) table_identifier
  150. ;
  151. declare continue handler FOR INCORRECT_VALUE_ERROR
  152. SELECT 'We hit warning 1366. It''s probably nothing serious.'
  153. ;
  154. declare exit handler FOR SQLEXCEPTION
  155. SELECT 'SQL Exception' error_type
  156. , 'Unexpected generic error. Debug the procedure.' error_message
  157. , concat(
  158. 'CALL ',schema(),'.p_create_federated_table('
  159. , IF( p_remote_host IS NULL
  160. , 'NULL'
  161. , concat('''',p_remote_host,'''')
  162. )
  163. ,',', IF( p_remote_port IS NULL
  164. , 'NULL'
  165. , p_remote_port
  166. )
  167. ,',', IF( p_remote_user IS NULL
  168. , 'NULL'
  169. , concat('''',p_remote_user,'''')
  170. )
  171. ,',', IF( p_remote_password IS NULL
  172. , 'NULL'
  173. , concat('''',p_remote_password,'''')
  174. )
  175. ,',', IF(
  176. p_remote_schema IS NULL
  177. , 'NULL'
  178. , concat('''',p_remote_schema,'''')
  179. )
  180. ,',', IF( p_remote_table IS NULL
  181. , 'NULL'
  182. , concat('''',p_remote_table,'''')
  183. )
  184. ,',', IF( p_local_schema IS NULL
  185. , 'NULL'
  186. , concat('''',p_local_schema,'''')
  187. )
  188. ,',', IF( p_local_table IS NULL
  189. , 'NULL'
  190. , concat('''',p_local_table,'''')
  191. )
  192. , ')'
  193. ) call_command
  194. ;
  195.  
  196. -- The following block creates temporary federated tables
  197. -- on the remote information_schema.
  198. -- These are needed to generate the structure
  199. -- of the local federated table.
  200. SELECT 'Getting remote metadata...' progress;
  201. begin
  202. -- Prefix used for the temporary tables
  203. declare v_temp_table_prefix char(25)
  204. DEFAULT 'p_create_federated_table$';
  205. -- Used to fetch the generated DDL from the cursor
  206. declare v_create_table_statement text;
  207. -- Cursor loop control variable
  208. declare v_no_more_rows BOOLEAN
  209. DEFAULT FALSE;
  210. -- Cursor generates DDL for createing temporary federated
  211. -- tables on the remote information_schema.
  212. -- We need this to generate the DDL to create the actual
  213. -- federated table specified by the user.
  214. declare csr_metadata cursor FOR
  215. SELECT table_name
  216. , concat(
  217. 'create temporary table'
  218. ,'\n',schema(),'.'
  219. ,v_temp_table_prefix,table_name,'('
  220. ,'\n',group_concat(
  221. column_name
  222. , ' '
  223. , column_type
  224. , IF(
  225. character_set_name IS NULL
  226. , ''
  227. , concat(
  228. ' character set '
  229. , character_set_name
  230. , ' collate '
  231. , collation_name
  232. )
  233. )
  234. , IF( is_nullable='NO'
  235. , ' NOT NULL'
  236. , ''
  237. )
  238. separator '\n,'
  239. )
  240. ,'\n',')'
  241. ,'\n','engine = federated'
  242. ,'\n','connection = '
  243. ,'\n',''''
  244. , v_connectstring
  245. , table_schema
  246. , '/'
  247. , table_name
  248. , ''''
  249. )
  250. FROM information_schema.COLUMNS
  251. WHERE table_schema = 'information_schema'
  252. AND table_name IN (
  253. 'COLUMNS'
  254. , 'STATISTICS'
  255. , 'TABLE_CONSTRAINTS'
  256. )
  257. GROUP BY table_schema
  258. , table_name
  259. ;
  260. -- handler to control the cursor loop
  261. declare continue handler FOR NOT FOUND
  262. SET v_no_more_rows := TRUE;
  263.  
  264. SET @drop_temporary_tables_statement := NULL;
  265.  
  266. -- loop through the cursor
  267. open csr_metadata;
  268. my_loop: loop
  269. -- get the DDL for the temporary federated
  270. -- information_schema table
  271. fetch csr_metadata
  272. INTO v_drop_table_name
  273. , v_create_table_statement;
  274.  
  275. -- basic cursor loop control exits if cursor is exhausted
  276. IF v_no_more_rows then
  277. close csr_metadata;
  278. leave my_loop;
  279. end IF;
  280. -- build a statement to drop all temporary tables
  281. SET @drop_temporary_tables_statement := IF (
  282. @drop_temporary_tables_statement IS NULL
  283. , concat(
  284. 'DROP TEMPORARY TABLE IF EXISTS '
  285. , v_temp_table_prefix
  286. , v_drop_table_name
  287. )
  288. , concat(
  289. @drop_temporary_tables_statement
  290. , ','
  291. , v_temp_table_prefix
  292. , v_drop_table_name
  293. )
  294. );
  295.  
  296. -- kludge: need a user variable to execute the DDL string
  297. -- dynamically with the PREPARE syntax
  298. SET @create_table_statement := v_create_table_statement;
  299.  
  300. -- create the temporary federated information_schema table
  301. -- select concat('executing: ', @create_table_statement);
  302.  
  303. prepare stmt FROM @create_table_statement;
  304. execute stmt;
  305. deallocate prepare stmt;
  306.  
  307. end loop;
  308. end;
  309. -- Reset the variable. Mainly to simplify debugging
  310. SELECT 'Generating CREATE TABLE statement for FEDERATED table...' progress;
  311. SET @create_table_statement:='...generating statement...';
  312. -- This creates the actual ddl for the requested local FEDERATED table.
  313. -- It selects the DDL directly into the user variable.
  314. -- It does this by querying the remote information_schema.
  315. -- This DDL includes the index definitions of the remote table.
  316. SELECT concat(
  317. 'create table if not exists'
  318. ,'\n','`',v_local_schema,'`'
  319. ,'.' ,'`',v_local_table,'`'
  320. , '('
  321. ,'\n',column_definitions
  322. , coalesce(index_definitions,'')
  323. ,'\n',')'
  324. ,'\n','engine = federated'
  325. ,'\n','connection = '
  326. ,'\n',''''
  327. , v_connectstring
  328. , column_definitions.table_schema
  329. , '/'
  330. , column_definitions.table_name
  331. , ''''
  332. ) stmt
  333. INTO @create_table_statement
  334. FROM (
  335. SELECT table_schema
  336. , table_name
  337. , group_concat(
  338. '`',column_name,'` '
  339. , column_type
  340. , IF(
  341. character_set_name IS NULL
  342. , ''
  343. , concat(
  344. ' character set '
  345. , character_set_name
  346. , ' collate '
  347. , collation_name
  348. )
  349. )
  350. , IF( is_nullable='NO'
  351. , ' not null'
  352. , ''
  353. )
  354. , IF( column_default IS NULL
  355. , ''
  356. , concat(
  357. ' default '
  358. , case
  359. when data_type = 'TIMESTAMP'
  360. AND column_default = 'CURRENT_TIMESTAMP'
  361. then column_default
  362. when data_type LIKE '%char'
  363. OR data_type LIKE 'date%'
  364. OR data_type LIKE 'time%'
  365. OR data_type IN ('set','enum')
  366. then concat('''',column_default,'''')
  367. else column_default
  368. end
  369. )
  370. )
  371. , IF(extra='','',concat(' ',extra))
  372. , ' comment '
  373. , '''',column_comment,''''
  374. separator '\n,'
  375. ) AS column_definitions
  376. FROM p_create_federated_table$columns
  377. WHERE table_schema = p_remote_schema
  378. AND table_name = p_remote_table
  379. GROUP BY table_schema
  380. , table_name
  381. ) column_definitions
  382. LEFT JOIN (
  383. SELECT index_definitions.table_schema
  384. , index_definitions.table_name
  385. , concat(
  386. '\n,'
  387. , group_concat(
  388. case c.constraint_type
  389. when 'PRIMARY KEY' then
  390. constraint_type
  391. when 'UNIQUE' then
  392. concat(
  393. 'CONSTRAINT '
  394. , constraint_name
  395. , ' '
  396. , constraint_type
  397. )
  398. else
  399. concat(
  400. IF( index_type IN (
  401. 'FULLTEXT'
  402. , 'SPATIAL'
  403. )
  404. , concat(
  405. index_type
  406. , ' '
  407. )
  408. , ''
  409. )
  410. , IF( non_unique
  411. , ''
  412. , 'UNIQUE '
  413. )
  414. , 'INDEX '
  415. , '`',index_name,'`'
  416. )
  417. end
  418. , index_columns
  419. , IF( index_type IN (
  420. 'BTREE'
  421. , 'HASH'
  422. )
  423. , concat(
  424. ' USING '
  425. , index_type
  426. )
  427. , ''
  428. )
  429. ORDER BY c.constraint_type
  430. separator '\n,'
  431. )
  432. ) AS index_definitions
  433. FROM (
  434. SELECT table_schema
  435. , table_name
  436. , index_name
  437. , index_type
  438. , non_unique
  439. , concat(
  440. '('
  441. , group_concat(
  442. '`',column_name,'`'
  443. , IF( sub_part IS NULL
  444. , ''
  445. , concat(
  446. '(',sub_part,')'
  447. )
  448. )
  449. ORDER BY seq_in_index
  450. )
  451. , ')'
  452. ) index_columns
  453. FROM p_create_federated_table$statistics
  454. WHERE table_schema = p_remote_schema
  455. AND table_name = p_remote_table
  456. AND index_type NOT IN ('FULLTEXT')
  457. GROUP BY table_schema
  458. , table_name
  459. , index_name
  460. , index_type
  461. , non_unique
  462. ) index_definitions
  463. LEFT JOIN (
  464. SELECT table_schema
  465. , table_name
  466. , constraint_name
  467. , constraint_type
  468. FROM p_create_federated_table$table_constraints c
  469. WHERE table_schema = p_remote_schema
  470. AND table_name = p_remote_table
  471. AND constraint_type IN (
  472. 'PRIMARY KEY'
  473. , 'UNIQUE'
  474. )
  475. GROUP BY table_schema
  476. , table_name
  477. , constraint_name
  478. , constraint_type
  479. ) c
  480. ON index_definitions.table_schema = c.table_schema
  481. AND index_definitions.table_name = c.table_name
  482. AND index_definitions.index_name = c.constraint_name
  483. GROUP BY table_schema
  484. , table_name
  485. ) index_definitions
  486. ON column_definitions.table_schema = index_definitions.table_schema
  487. AND column_definitions.table_name = index_definitions.table_name
  488. ;
  489.  
  490. -- Create the actual FEDERATED table by dynamically executing
  491. -- the generated DDL for the requested FFEDERATED table.
  492. SELECT concat('Creating FEDERATED table. SQL: ', @create_table_statement) progress;
  493.  
  494. prepare stmt FROM @create_table_statement;
  495. execute stmt;
  496. deallocate prepare stmt;
  497.  
  498. -- Print a friendly message that we succeeded
  499. SELECT 'Success' completion_type
  500. , concat(
  501. 'Created FEDERATED table '
  502. , v_connectstring,'/',p_remote_schema,'/',p_remote_table
  503. ) completion_message
  504. , concat(v_local_schema,'.',v_local_table) table_identifier
  505. ;
  506. end;
  507.  
  508. -- Cleanup: restore the original sql mode
  509. SET @@sql_mode := v_old_sql_mode;
  510. -- Cleanup: restore the original GROUP_CONCAT buffer size
  511. SET @@group_concat_max_len := v_old_group_concat_max_len;
  512.  
  513. -- Cleanup: drop the temporary federated information_schema tables.
  514. prepare stmt FROM @drop_temporary_tables_statement;
  515. execute stmt;
  516. deallocate prepare stmt;
  517.  
  518. -- Cleanup: reset the user defined variables.
  519. SET @create_table_statement := NULL
  520. , @drop_temporary_tables_statement := NULL
  521. ;
  522. end;
  523. $$
  524.  
  525. delimiter ;

Current Tags

You must be logged in to tag this tool

No Comments yet

Votes

  • Rated 4.80 out of 5
Rated 4.80 out of 5 with 5 votes cast.
You must be logged in to vote.

Watches

6 members are watching this tool
You must be logged in to track this tool.

Provide Feedback

Please note:
HTML will be purified, but we allow for a number of HTML tags so that you have the flexibility to decorate your comment text to some extent. The comments allow the following HTML tags:

strong, b, em, blockquote, a, code, pre

To put code into your comment, simply encapsulate your code with
[code language="XXX"][/code], where XXX is any common language, for instance "PHP", "SQL", "C", etc.



You must be logged in to comment