On a Bash script I want to keep MySQL sessions open across several sequential accesses; the common way to access MySQL is by opening an individual session for each SQL command, or set of commands, such as
mysql -u user -e "show tables;"
The limitation of this method is the loss of atomicity and lock statuses for those transactions which need to be twofold: for example, it's not possible to preserve the lock status on a table T
for the whole length of the following twofold operation:
### Minimalistic example
data=$(mysql -e "\
lock table T write;
select col from T;
")
# ...
# parse 'data' and compute 'output' variable
# ...
mysql -e "insert into T values ($output);"
My approach to the solution is to keep the MySQL session open across multiple accesses by using two FIFOs and hang the process on background.
Proposed solution:
Create the pair of FIFOs: mkfifo IN OUT
.
Set the MySQL-client instance in place, along with a dummy while
to keep the pipes open and prevent SIGPIPE
signals:
mysql --xml --batch --raw --skip-column-names \
-h "$hostname" -u "$username" "$db" >IN <OUT &
while :; do sleep 1; done <IN >OUT &
Then test it:
echo "show tables;" >OUT
read <IN
Result:
It does not work. The echo
command completes and bash steps over it, which means MySQL receives the input, but read
hangs forever, so no output is produced.
I discovered that eliminating the IN
FIFO the whole task doesn't hang:
mysql --xml --batch --raw --skip-column-names \
-h "$hostname" -u "$username" "$db" <OUT &
while :; do sleep 1; done >OUT &
echo "show tables;" >OUT # this produces the expected output
Is this behavior expected? Also I wonder if it is possible to run twofold operations in Bash without custom homebrews.
I know this thread is old, but I was also looking for a comfortable bash mysql session implementation and didn't found something good enough for my needs, so I wrote my own one which I'd like to share with the world.
NOTES:
Example how to loop the returned data of a two column query
eg. "SELECT dt_id, dt_name FROM ..."
I developed a cheat for this kind of problem a while ago, using a unix socketpair(). It will only persist while the script is running (even in the background), but it is a lot easier to deal with that FIFOs.
Below is a complete example, with the source code to the binary included. Don't be daunted, you can play around with the idea very easily within an interactive shell:
Here's a simple script:
and I include the source code within the script, just incase I move the script somewhere and I can't find the binary :)
The problem with FIFOs is that when every process that is inputting data terminates, it signals the processes that are reading (in this case
mysql
) that it's the end of the data, so it terminates.The trick is to make sure that there is a process keeping the FIFO input alive at all times. You can do that by running a
sleep 999999999 > fifofile
in background.Exemple:
In the end we terminate the
sleep
process to release the FIFO input completely. It will signalmysql
that the input has ended and it will automatically die in consequence.There is also an alternative that don't require FIFOs, but you will need two scripts:
run.sh:
querygenerator.sh:
Here is a simple example that reproduces the locked-up behavior you describe:
The
echo
command completes but neithercat
norsed
do. Maybe playing with this example will yield some results.I am not sure you need to do it this way, though. Maybe it would be better to have a control script that pipes to MySQL, and have MySQL write its output to a file.
Control flow via FIFOs is more appealing than waiting for files to show up in a
while
loop; but FIFOs are surprisingly hard to get right.