<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>MySQL Preacher &#187; bash</title>
	<atom:link href="http://mysqlpreacher.com/wordpress/tag/bash/feed/" rel="self" type="application/rss+xml" />
	<link>http://mysqlpreacher.com/wordpress</link>
	<description>Because Sharing is Caring</description>
	<lastBuildDate>Mon, 26 Sep 2011 23:34:16 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.1.4</generator>
		<item>
		<title>Automating MySQL access with expect and bash scripting</title>
		<link>http://mysqlpreacher.com/wordpress/2010/02/automating-mysql-access-with-expect-and-bash-scripting/</link>
		<comments>http://mysqlpreacher.com/wordpress/2010/02/automating-mysql-access-with-expect-and-bash-scripting/#comments</comments>
		<pubDate>Mon, 08 Feb 2010 17:08:26 +0000</pubDate>
		<dc:creator>Darren Cassar</dc:creator>
				<category><![CDATA[Databases]]></category>
		<category><![CDATA[Intermediate]]></category>
		<category><![CDATA[OS]]></category>
		<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[access]]></category>
		<category><![CDATA[automation]]></category>
		<category><![CDATA[bash]]></category>
		<category><![CDATA[expect]]></category>
		<category><![CDATA[MySQL]]></category>

		<guid isPermaLink="false">http://mysqlpreacher.com/wordpress/?p=327</guid>
		<description><![CDATA[If you have multiple database servers with strange names, or if you have to hop over multiple machines to connect to any mysql database server, then you know what a pain it can be to administer such a setup. Thanks to some scripting, you can automate such tasks as follows: Create an expect script: /path/to/sshmysql.exp [...]]]></description>
			<content:encoded><![CDATA[<p>If you have multiple database servers with strange names, or if you have to hop over multiple machines to connect to any mysql database server, then you know what a pain it can be to administer such a setup. Thanks to some scripting, you can automate such tasks as follows:</p>
<p>Create an expect script:<br />
/path/to/sshmysql.exp</p>
<blockquote><p>#!/usr/bin/expect -f<br />
#script by darren cassar<br />
#mysqlpreacher.com</p>
<p>set machine  [lindex $argv 0]</p>
<p>set timeout -1</p>
<p>spawn ssh username@$machine<br />
match_max 100000<br />
expect -exact &#8220;assword: &#8220;<br />
send &#8212; &#8220;password\r&#8221;<br />
send &#8212; &#8220;sudo -k; sudo su &#8211; mysql\r&#8221;<br />
expect -exact &#8220;sudo -k; sudo su &#8211; mysql&#8221;<br />
expect -exact &#8220;assword:&#8221;<br />
send &#8212; &#8220;password\r&#8221;<br />
interact</p></blockquote>
<p># you should change the word password in &#8216;send &#8212; &#8220;password\r&#8221;&#8216; to your login password<br />
# if you have the same password for each environment you could also script logging into mysql directly from the same expect script BUT that is not recommended.</p>
<p>Create a bash script:<br />
/path/to/login.sh</p>
<blockquote><p>#!/bin/bash<br />
#script by darren cassar<br />
#mysqlpreacher.com</p>
<p>sm=&#8217;/path/to/sshmysql.exp&#8217;</p>
<p>menu() {<br />
  echo &#8221; 101 &#8211; dev.databaseserver1 &#8220;<br />
  echo &#8221; 102 &#8211; dev.databaseserver2 &#8220;<br />
  echo &#8221; 103 &#8211; dev.databaseserver3 &#8220;<br />
  echo &#8221; 201 &#8211; qa.databaseserver1 &#8220;<br />
  echo &#8221; 301 &#8211; uat.databaseserver1 &#8220;<br />
  echo &#8221; 302 &#8211; uat.databaseserver2 &#8220;<br />
  echo &#8221; 401 &#8211; prod.databaseserver1 &#8220;<br />
  echo &#8221; &#8220;<br />
}</p>
<p>ARGUMENT=notmenu</p>
<p>if [ -z "$1" ]<br />
  then<br />
    ARGUMENT=menu<br />
else<br />
  choice=$1<br />
fi</p>
<p>if [ $ARGUMENT = "menu" ]<br />
  then<br />
    menu<br />
else<br />
  case &#8220;$choice&#8221; in<br />
  101|dev.databaseserver1   ) $sm dev.databaseserver1;;<br />
  102|dev.databaseserver2   ) $sm dev.databaseserver2;;<br />
  103|dev.databaseserver3   ) $sm dev.databaseserver3;;<br />
  201|qa.databaseserver1   ) $sm qa.databaseserver1;;<br />
  301|uat.databaseserver1   ) $sm uat.databaseserver1;;<br />
  302|uat.databaseserver2   ) $sm uat.databaseserver2;;<br />
  401|prod.databaseserver1   ) $sm prod.databaseserver1;;<br />
  *        ) echo &#8220;Wrong value passed to script&#8221;<br />
             menu ;;<br />
  esac<br />
fi</p></blockquote>
<blockquote><p>alias l=&#8217;/path/to/login.sh&#8217;</p></blockquote>
<p>Output: </p>
<blockquote><p>[darrencassar@mymachine ~ ]$ l<br />
 101 &#8211; dev.databaseserver1<br />
 102 &#8211; dev.databaseserver2<br />
 103 &#8211; dev.databaseserver3<br />
 201 &#8211; qa.databaseserver1<br />
 301 &#8211; uat.databaseserver1<br />
 302 &#8211; uat.databaseserver2<br />
 401 &#8211; prod.databaseserver1</p></blockquote>
<p>Output:<br />
The below command would log you into the first development database server as mysql user.</p>
<blockquote><p>[darrencassar@mymachine ~ ]$ l 101 </p></blockquote>
<p>On each machine place aliases for each instance in the .profile</p>
<blockquote><p>alias use3306=&#8217;mysql -u root -p -h 127.0.0.1 -P 3306 &#8211;prompt=&#8221;mysql \D> &#8220;&#8216;</p></blockquote>
<p>The above setup can be used using any client/server OS: Linux, Solaris, MAC OS or Windows(running Cygwin)</p>
<p><strong>NOTE: If you store the password in clear text inside the expect script, you should at least save the scripts inside an encrypted partition on your machine and make sure that folder is not shared or accessible by anyone. Another way of doing it would be to use either SSHKeys OR save the password inside a file and encrypt it using <a href="http://www.madboa.com/geek/openssl/#encrypt-simple" target="_blank">OpenSSL</a></strong></p>
<p>Enjoy!</p>
]]></content:encoded>
			<wfw:commentRss>http://mysqlpreacher.com/wordpress/2010/02/automating-mysql-access-with-expect-and-bash-scripting/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Replicating from MySQL to *</title>
		<link>http://mysqlpreacher.com/wordpress/2009/05/replicating-from-mysql-to/</link>
		<comments>http://mysqlpreacher.com/wordpress/2009/05/replicating-from-mysql-to/#comments</comments>
		<pubDate>Fri, 29 May 2009 16:28:28 +0000</pubDate>
		<dc:creator>Darren Cassar</dc:creator>
				<category><![CDATA[Advanced]]></category>
		<category><![CDATA[Intermediate]]></category>
		<category><![CDATA[MySQL]]></category>
		<category><![CDATA[bash]]></category>
		<category><![CDATA[mysqlbinlog]]></category>
		<category><![CDATA[parsing]]></category>
		<category><![CDATA[replication]]></category>
		<category><![CDATA[row based replication]]></category>
		<category><![CDATA[statement based replication]]></category>

		<guid isPermaLink="false">http://mysqlpreacher.com/wordpress/?p=175</guid>
		<description><![CDATA[Recently I needed to replicate between MySQL and another database technology. You might say, why on earth would you want to do something like that, but believe me there are reasons and definitely not (to go away from MySQL to some other DB technology like Oracle or SQL server). Unsurprisingly there are quite a few different tools to do it from any platform towards MySQL but very few which do it the other way round, just to name a couple: Golden Gate and DSCallards.]]></description>
			<content:encoded><![CDATA[<p><strong>Obtaining ansi SQL from RBR.</strong></p>
<p>Recently I needed to replicate between MySQL and another database technology. You might say, why on earth would you want to do something like that, but believe me there are reasons and definitely not (to go away from MySQL to some other DB technology like Oracle or SQL server). Unsurprisingly there are quite a few different tools to do it from any platform towards MySQL but very few which do it the other way round, just to name a couple: Golden Gate and DSCallards.</p>
<p>Whilst not going into their tools (you can find more information on their websites), HIT from DSCallards needs its software to run on Windows and Golden Gate is an expensive beast which was too much for what I needed, thus I decided to have a look at doing the job myself. Although it might look an overkill to do so, it took me a few hours to find a solution and implement it and a couple more to test it and here is a simple description.</p>
<p>MySQL replication can be SBR (statement based), RBR (row based) or a mixture of both. Now despite the fact that the mixture provides the best performance, it would be the most complicated in order to achieve a home made solution, and SBR in my opinion would have also been a bit of a headache to make sure queries didn&#8217;t contain non ansi sql through the use of functions like now(), sysdate() and many others. I therefore decided that RBR would be the option of choice.</p>
<p>Although I wonder how many of you ever read an RBR binlog using `mysqlbinlog mysql-bin.000004`, it would be something like:</p>
<blockquote><p>
#090526 14:09:13 server id 1  end_log_pos 1420  Query   thread_id=192   exec_time=0     error_code=0<br />
SET TIMESTAMP=1243343353/*!*/;<br />
BEGIN<br />
/*!*/;<br />
# at 1420<br />
# at 1464<br />
#090526 14:09:13 server id 1  end_log_pos 1464  Table_map: `test`.`t2` mapped to number 21<br />
#090526 14:09:13 server id 1  end_log_pos 1498  Write_rows: table id 21 flags: STMT_END_F</p>
<p>BINLOG &#8216;<br />
+ekbShMBAAAALAAAALgFAAAAABUAAAAAAAAABHRlc3QAAnQyAAID/gL+AQM=<br />
+ekbShcBAAAAIgAAANoFAAAQABUAAAAAAAEAAv/+AQAAAA==<br />
&#8216;/*!*/;<br />
# at 1498<br />
#090526 14:09:13 server id 1  end_log_pos 1567  Query   thread_id=192   exec_time=0     error_code=0<br />
SET TIMESTAMP=1243343353/*!*/;<br />
COMMIT<br />
/*!*/;<br />
# at 1567<br />
#090526 14:09:38 server id 1  end_log_pos 1635  Query   thread_id=192   exec_time=0     error_code=0<br />
SET TIMESTAMP=1243343378/*!*/;<br />
BEGIN<br />
/*!*/;<br />
# at 1635<br />
# at 1679<br />
#090526 14:09:38 server id 1  end_log_pos 1679  Table_map: `test`.`t2` mapped to number 21<br />
#090526 14:09:38 server id 1  end_log_pos 1733  Update_rows: table id 21 flags: STMT_END_F</p>
<p>BINLOG &#8216;<br />
EuobShMBAAAALAAAAI8GAAAAABUAAAAAAAAABHRlc3QAAnQyAAID/gL+AQM=<br />
EuobShgBAAAANgAAAMUGAAAQABUAAAAAAAEAAv///AQAAAABZPwCAAAAAWT+AQAAAP4CAAAA<br />
&#8216;/*!*/;<br />
# at 1733<br />
#090526 14:09:38 server id 1  end_log_pos 1802  Query   thread_id=192   exec_time=0     error_code=0<br />
SET TIMESTAMP=1243343378/*!*/;<br />
COMMIT
</p></blockquote>
<p>Now that isn&#8217;t the most readable text you ever seen right? As Giuseppe said &#8220;This is more difficult to read than ancient Etruscan. If you are a DBA, you curse and look for help.&#8221; But the replication guys at mysql created a nice -v for us to add to mysqlbinlog thus issuing `mysqlbinlog -v mysql-bin.000004` would result in the following:</p>
<blockquote><p>
#090526 14:09:13 server id 1  end_log_pos 1420  Query   thread_id=192   exec_time=0     error_code=0<br />
SET TIMESTAMP=1243343353/*!*/;<br />
BEGIN<br />
/*!*/;<br />
# at 1420<br />
# at 1464<br />
#090526 14:09:13 server id 1  end_log_pos 1464  Table_map: `test`.`t2` mapped to number 21<br />
#090526 14:09:13 server id 1  end_log_pos 1498  Write_rows: table id 21 flags: STMT_END_F</p>
<p>BINLOG &#8216;<br />
+ekbShMBAAAALAAAALgFAAAAABUAAAAAAAAABHRlc3QAAnQyAAID/gL+AQM=<br />
+ekbShcBAAAAIgAAANoFAAAQABUAAAAAAAEAAv/+AQAAAA==<br />
&#8216;/*!*/;<br />
### INSERT INTO test.t2<br />
### SET<br />
###   @1=1<br />
###   @2=NULL<br />
# at 1498<br />
#090526 14:09:13 server id 1  end_log_pos 1567  Query   thread_id=192   exec_time=0     error_code=0<br />
SET TIMESTAMP=1243343353/*!*/;<br />
COMMIT<br />
/*!*/;<br />
# at 1567<br />
#090526 14:09:38 server id 1  end_log_pos 1635  Query   thread_id=192   exec_time=0     error_code=0<br />
SET TIMESTAMP=1243343378/*!*/;<br />
BEGIN<br />
/*!*/;<br />
# at 1635<br />
# at 1679<br />
#090526 14:09:38 server id 1  end_log_pos 1679  Table_map: `test`.`t2` mapped to number 21<br />
#090526 14:09:38 server id 1  end_log_pos 1733  Update_rows: table id 21 flags: STMT_END_F</p>
<p>BINLOG &#8216;<br />
EuobShMBAAAALAAAAI8GAAAAABUAAAAAAAAABHRlc3QAAnQyAAID/gL+AQM=<br />
EuobShgBAAAANgAAAMUGAAAQABUAAAAAAAEAAv///AQAAAABZPwCAAAAAWT+AQAAAP4CAAAA<br />
&#8216;/*!*/;<br />
### UPDATE test.t2<br />
### WHERE<br />
###   @1=4<br />
###   @2=&#8217;d&#8217;<br />
### SET<br />
###   @1=2<br />
###   @2=&#8217;d&#8217;<br />
### UPDATE test.t2<br />
### WHERE<br />
###   @1=1<br />
###   @2=NULL<br />
### SET<br />
###   @1=2<br />
###   @2=NULL<br />
# at 1733<br />
#090526 14:09:38 server id 1  end_log_pos 1802  Query   thread_id=192   exec_time=0     error_code=0<br />
SET TIMESTAMP=1243343378/*!*/;<br />
COMMIT
</p></blockquote>
<p>The exact same output with some decently readable output. The problem at this point is that the output is not really something any other technology would undestand (not even feeding it to MySQL would work! &#8230;.</p>
<p>At this point I needed to do some compromises in order to reduce the complexity of this job, i.e. I will not be creating and altering tables / databases or indexes during runtime (this I can do without as I can do the same things on the slave manually when I need to do anything like that on the master) and the replication won&#8217;t be in real time i.e. the slave will be fed the sql periodically through a script. The last compromise wasn&#8217;t actually a compromise but a decision based on speed of coding as I&#8217;m more proficient in bash than perl and as such I decided to go with bash as a proof of concept that this thing can be done. This would never happen in reality as it would be much slower as compared to coding the same thing in perl or C / C++ (choice is up to you).</p>
<p>Now parsing the binary logs is not rocket science is it? As you can see there are three hashes &#8216;###&#8217; in front of the readable query so a simple grep is fine. A few subsitutions and text processing and you&#8217;ll end up with:</p>
<blockquote><p>
darrencassar@mysqlpreacher $ /home/dcassar/sandbox/5.1.30/bin/mysqlbinlog  | sed &#8216;s/^ *//g&#8217; | tr &#8216;\015\012&#8242; &#8216;\020 &#8216; | sed &#8216;s/ INSERT/;\nINSERT/g&#8217; | sed &#8216;s/ DELETE/;\nDELETE/g&#8217; | sed &#8216;s/ UPDATE/;\nUPDATE/g&#8217; | sed &#8216;${/^$/!s/$/;\<br />
&gt; /;}&#8217;<br />
INSERT INTO test.t1 SET @1=1 @2=&#8217;a';<br />
DELETE FROM test.t1 WHERE @1=1 @2=&#8217;a';<br />
INSERT INTO test.t2 SET @1=2 @2=&#8217;d';<br />
UPDATE test.t2 WHERE @1=2 @2=&#8217;d&#8217; SET @1=4 @2=&#8217;d';<br />
INSERT INTO test.t2 SET @1=1 @2=NULL;<br />
UPDATE test.t2 WHERE @1=4 @2=&#8217;d&#8217; SET @1=2 @2=&#8217;d';<br />
UPDATE test.t2 WHERE @1=1 @2=NULL SET @1=2 @2=NULL ;<br />
darrencassar@mysqlpreacher $
</p></blockquote>
<p>That is more readable but still not correctly formatted for our ANSI SQL slave.</p>
<blockquote><p>
`INSERT INTO test.t1 SET @1=1 @2=&#8217;a';` would need to be replaced with `INSERT INTO test.t1 (cola, colb) values(1,&#8217;a');` or `INSERT INTO test.t1 values(1,&#8217;a');`,<br />
`DELETE FROM test.t1 WHERE @1=1 @2=&#8217;a';` would need to be replaced with `DELETE FROM test.t1 WHERE cola=1 AND colb=&#8217;a';` and<br />
`UPDATE test.t2 WHERE @1=2 @2=&#8217;d&#8217; SET @1=4 @2=&#8217;d';` would have to become `UPDATE test.t2 SET cola=4 , colb=&#8217;d&#8217; WHERE cola=2 AND colb=&#8217;d';`
</p></blockquote>
<p>The above means we need to replace @1 and @2 with the appropriate column names done using a lookup table in my case by placing a files in data/dbname each bearing names of the different tables and listing each column in order:<br />
I.E. for a database named test and table named table1 having columns cola and colb I used the database folder named test in the mysql data folder and placed a file named table1, the contents of which were:</p>
<blockquote><p>cola<br />
colb</p></blockquote>
<p>The following piece of code does the job of replacing those dreadful @1, @2 etc with proper table names:</p>
<blockquote><p>
for (( i=1; i&lt;=`wc -l $dbname/$tbname | cut -d &#8221; &#8221; -f 1`; i++ ))<br />
do<br />
columnname=`awk &#8216;NR==a&#8217; a=$i $dbname/$tbname`<br />
LINE_TEMP=`echo $LINE_TEMP | sed &#8216;s/@&#8217;$i&#8217;=/AND &#8216;$columnname&#8217;=/g&#8217; | sed &#8216;s/SET AND/SET/g&#8217; | sed &#8216;s/WHERE AND/WHERE/g&#8217;`<br />
done
</p></blockquote>
<p>If you were thinking how I extracted dbname and tbname, here it is:</p>
<blockquote><p>
dbname=`echo $LINE | cut -d &#8221; &#8221; -f$col | cut -d &#8220;.&#8221; -f 1`<br />
tbname=`echo $LINE | cut -d &#8221; &#8221; -f$col | cut -d &#8220;.&#8221; -f 2`
</p></blockquote>
<p>where $LINE is each line extracted using the first command parsing mysqlbinary output and the variable col is 3 for delete and 2 for update which reflects the positioning of the database name and table name inside the extracted lines.</p>
<p>A rather ugly way of re-ordering the update command yet functional is:</p>
<blockquote><p>
echo $LINE_TEMP | cut -d &#8220;.&#8221; -f2 | sed &#8216;s/&#8217;$tbname&#8217; //&#8217; | sed &#8216;s/;//&#8217; &gt; templinefile<br />
cat templinefile | sed &#8216;s/ SET /\nSET /g&#8217; &gt; templinefile2<br />
LINE_TEMP=&#8221;UPDATE $dbname.$tbname `tail -1 templinefile2  | sed &#8216;s/ is NULL/=NULL/g&#8217; | sed &#8216;s/AND/,/g&#8217;` `head -1 templinefile2`;&#8221;
</p></blockquote>
<p>The last thing to remember is taking care of `=NULL` and replace it by `is NULL`.</p>
<blockquote><p>
LINE_TEMP=`echo $LINE_TEMP | sed &#8216;s/=NULL/ is NULL/g&#8217;`
</p></blockquote>
<p>As I said this was a fast proof of concept rather than a full fledged optimized script doing the job! The total length of code is 37 lines (excluding comments but with correct and nead formatting).</p>
<p>Enjoy<br />
Darren</p>
]]></content:encoded>
			<wfw:commentRss>http://mysqlpreacher.com/wordpress/2009/05/replicating-from-mysql-to/feed/</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
	</channel>
</rss>

