logo

Multi-query function

Add comment

I needed a function that will take a string or a file and then split it into small SQL queries. I found few functions on Internet but they did not work as i expected. They all had following principle of work:

  1. Take a string
  2. Use function explode and “;” as a delimiter to create an array
  3. Execute each row in array

I believe that already at first glance many of you see that it does not go so easily. The first and obvious problem is incorrectly split. If we have a string which contains “;” it will be divided in 2 parts and this will lead to errors in MySQL. Another problem is the incompatibility with the sql dump files. Suppose you have a procedure in your sql backup. In front of the procedure is the command DELIMITER that tells us that now follows new delimiter. Since none of these functions there does not have part of the code responsible for processing DELIMITER commands, SQL code will break up, and again we’ll get MySQL errors.

So I decided to make my own function. Since I’ve made it, I decided to show you how and which part of code is responsible for what.

So let’s start then.

First, we declare the function. Function will receive two arguments. First is the SQL code that must be processed and the second is a string that represents the delimiter.

function multyQuery ( $queryBlock, $delimiter = ';' ) {

}
Now we declare some variables that will help us in the processing code.
  • $inString – If we are in string it will be true, by default it is false
  • $escChar – If “\”is found it will be true, by default it is false
  • $sql – Variable that will contain uncompleted SQL query
  • $stringChar – Character with which we entered the string
  • $queryLine – Array with pa parsed SQL queries
  • $sqlRows – Our code exploded by ‘\n’
  • $delimiterLen – Delimiter length
function multyQuery ( $queryBlock, $delimiter = ';' ) {
	$inString = false;
	$escChar = false;
	$sql = '';
	$stringChar = '';
	$queryLine = array();
	$sqlRows = split ( "\n", $queryBlock );
	$delimiterLen = strlen ( $delimiter );
}

Now, when we have our initial variables declared, we can continue. Now we need a loop that will pass through our code that is divided into smaller sections (each new line in the code and a new row in array). To loop we use do…while command.Inside the loop we will declare a variable $sqlRow that will contain the current value of a key in a row on which the loop stopped. Since we used explode and “\n” to devide our string, now we have to return “\n” back in string. Then we declare variable $sqlRowLen that will containt $sqlRow length. That variable we will use in for loop.

This is what we have fro now.

function multyQuery ( $queryBlock, $delimiter = ';' ) {
	$inString = false;
	$escChar = false;
	$sql = '';
	$stringChar = '';
	$queryLine = array();
	$sqlRows = split ( "\n", $queryBlock );
	$delimiterLen = strlen ( $delimiter );

	do {
		sqlRow = current ( $sqlRows ) . "\n";
		$sqlRowLen = strlen ( $sqlRow );

	} while ( next( $sqlRows ) !== false );
}

Now we’ll create our loop. Loop goes from 0 and goes to value of $sqlRowLen (row length). First thing we’ll do in loop is tho check if current character is comment. If it is, we skip it and go to th next character. It ist’s not then we save our character in $znak variable.

function multyQuery ( $queryBlock, $delimiter = ';' ) {
	$inString = false;
	$escChar = false;
	$sql = '';
	$stringChar = '';
	$queryLine = array();
	$sqlRows = split ( "\n", $queryBlock );
	$delimiterLen = strlen ( $delimiter );

	do {
		sqlRow = current ( $sqlRows ) . "\n";
		$sqlRowLen = strlen ( $sqlRow );
		for ( $i = 0; $i < $sqlRowLen; $i++ ) {
			if ( ( substr ( ltrim ( $sqlRow ), $i, 2 ) === '--' || substr ( ltrim ( $sqlRow ), $i, 1 ) === '#' ) && !$inString ) {
				break;
			}
			$znak = substr ( $sqlRow, $i, 1 );
		}

	} while ( next( $sqlRows ) !== false );
}

Now we do some parsing. First we check if our character is “ or ‘. It that’s true then we are entering or exiting string. If we are in string,  we check if previous character was “\”  (escape character). If it’s not, and current character equals to character with which we entered the string then we are exiting string. Varijablu $inString mijenjamu u false. If we are not in string then we are entering in string. Variable $inString is true and variable $stringChar is our current character ($znak).

function multyQuery ( $queryBlock, $delimiter = ';' ) {
	$inString = false;
	$escChar = false;
	$sql = '';
	$stringChar = '';
	$queryLine = array();
	$sqlRows = split ( "\n", $queryBlock );
	$delimiterLen = strlen ( $delimiter );

	do {
		sqlRow = current ( $sqlRows ) . "\n";
		$sqlRowLen = strlen ( $sqlRow );
		for ( $i = 0; $i < $sqlRowLen; $i++ ) {
			if ( ( substr ( ltrim ( $sqlRow ), $i, 2 ) === '--' || substr ( ltrim ( $sqlRow ), $i, 1 ) === '#' ) && !$inString ) {
				break;
			}
			$znak = substr ( $sqlRow, $i, 1 );
			if ( $znak === '\'' || $znak === '"' ) {
				if ( $inString ) {
					if ( !$escChar && $znak === $stringChar ) {
						$inString = false;
					}
				}
				else {
					$stringChar = $znak;
					$inString = true;
				}
			}
		}

	} while ( next( $sqlRows ) !== false );
}

Now we check if our charavter is “\”. If that's true, then next character is escaped. We also check if last character is alse “\”. If that's true then those two characters negate each other and $escChar is false. But if condition is true then $echChar has opposite value then it is now (true is false and false is true).

function multyQuery ( $queryBlock, $delimiter = ';' ) {
	$inString = false;
	$escChar = false;
	$sql = '';
	$stringChar = '';
	$queryLine = array();
	$sqlRows = split ( "\n", $queryBlock );
	$delimiterLen = strlen ( $delimiter );

	do {
		sqlRow = current ( $sqlRows ) . "\n";
		$sqlRowLen = strlen ( $sqlRow );
		for ( $i = 0; $i < $sqlRowLen; $i++ ) {
			if ( ( substr ( ltrim ( $sqlRow ), $i, 2 ) === '--' || substr ( ltrim ( $sqlRow ), $i, 1 ) === '#' ) && !$inString ) {
				break;
			}
			$znak = substr ( $sqlRow, $i, 1 );
			if ( $znak === '\'' || $znak === '"' ) {
				if ( $inString ) {
					if ( !$escChar && $znak === $stringChar ) {
						$inString = false;
					}
				}
				else {
					$stringChar = $znak;
					$inString = true;
				}
			}
			if ( $znak === '\\' && substr ( $sqlRow, $i - 1, 2 ) !== '\\\\' ) {
				$escChar = !$escChar;
			}
			else {
				$escChar = false;
			}
		}

	} while ( next( $sqlRows ) !== false );
}
Now comes a part which checks whether we have reached the end of the SQL query. First we check if current char equals to delimiter. You cans see we use substr and variable $delimiterLen. That is because our delimiter isn't allways single character. We just look if part of the string from current position to current position + delimiter length is our delimiter. If that's true we check if we are in string. If we are not then our SQL query is compleate and we can save it. But first little cleanup. We remove all whitespaces. Then we check if our SQL query sets up a new delimiter. To check that we use function preg_match. If that's true we save our new delimiter in variable $delimiterMacth in key 1. Now we take new delimiter length and save it into $delimiterLen. If sql query isn't DELIMITER we just save it to $queryLine, we empty our temporarily SQL code ($sql becomes empty string) and we go to the next character. In case that current character isn't equal to $delimiter we just add current character to temporarily SQL.

Now we just have to return our $queryLine that has all SQL queries that where in $queryBlock.

function multyQuery ( $queryBlock, $delimiter = ';' ) {
	$inString = false;
	$escChar = false;
	$sql = '';
	$stringChar = '';
	$queryLine = array();
	$sqlRows = split ( "\n", $queryBlock );
	$delimiterLen = strlen ( $delimiter );
	do {
		$sqlRow = current ( $sqlRows ) . "\n";
		$sqlRowLen = strlen ( $sqlRow );
		for ( $i = 0; $i < $sqlRowLen; $i++ ) {
			if ( ( substr ( ltrim ( $sqlRow ), $i, 2 ) === '--' || substr ( ltrim ( $sqlRow ), $i, 1 ) === '#' ) && !$inString ) {
				break;
			}
			$znak = substr ( $sqlRow, $i, 1 );
			if ( $znak === '\'' || $znak === '"' ) {
				if ( $inString ) {
					if ( !$escChar && $znak === $stringChar ) {
						$inString = false;
					}
				}
				else {
					$stringChar = $znak;
					$inString = true;
				}
			}
			if ( $znak === '\\' && substr ( $sqlRow, $i - 1, 2 ) !== '\\\\' ) {
				$escChar = !$escChar;
			}
			else {
				$escChar = false;
			}
			if ( substr ( $sqlRow, $i, $delimiterLen ) === $delimiter ) {
				if ( !$inString ) {
					$sql = trim ( $sql );
					$delimiterMatch = array();
					if ( preg_match ( '/^DELIMITER[[:space:]]*([^[:space:]]+)$/i', $sql, $delimiterMatch ) ) {
						$delimiter = $delimiterMatch [1];
						$delimiterLen = strlen ( $delimiter );
					}
					else {
						$queryLine [] = $sql;
					}
					$sql = '';
					continue;
				}
			}
			$sql .= $znak;
		}
	} while ( next( $sqlRows ) !== false );

	return $queryLine;
}

That would be our final function. She returns array with parsed queries and now you can go throught then and execute each one. Leave a comment if you have problems or you've discovered a bug (I tested it with various MySQL dump files and they where all successuful).

Related Posts
  • 21.05.2010 -- Multi-Language Site (6)
    It's been a while since I last posted something because I was very busy. So let's do something usefu...
  • 13.04.2009 -- 5 useful functions (1)
    I would like to share with you a couple of functions that use a lot and I consider them quite useful...
  • 27.07.2010 -- Upload Images With MySQL (0)
    [tinytoc level="1"]Intro[/tinytoc] Ok, let's continue with tutorial requests. This tutorial will ...
  • 07.04.2010 -- Edit XML (15)
    I had an idea about creating tutorial that will cover manipulating XML in PHP. The idea was to creat...
  • 02.01.2010 -- Enable E-mail In PHP – Win (0)
    This will be a quick tutorial that will show you how to enable e-mail function in PHP on Windows....
  • 16.10.2009 -- PHP DomDocument Tutorial (15)
    This will be a quick tutorial that will show you how to use PHP's DOMDocument to parse your XML so y...
  • 25.09.2009 -- PHP Confirm Registration Link (6)
    This will be quick tutorial on how you can implement confirm registration link into your PHP applica...

logo

6 comments to “Multi-query function”

  1. shaffy says:

    Nice tutorial.

  2. Let’s say you have this kind of code

    $code = 'INESRT INTO `someTable` (`id`) VALUES (1); '
    . 'INESRT INTO `someTable` (`id`) VALUES (2); '
    . 'INESRT INTO `someTable` (`id`) VALUES (3); '
    . 'INESRT INTO `someTable` (`id`) VALUES (4); ';

    If you’d try to execute this code using mysql_query function you’d get error.

    If you pass this code to function like

    $result = multyQuery($code);

    $result would be an array with 4 rows and each row would contain one query so you could do this.

    foreach ($result as $query)
        mysql_query($query);

    Then you would not get any errors :) .

  3. sony says:

    sory i new learn about this….., how can i test this function?? any example??

    thx. :) )

  4. How do you mean others? Just have this one :) .

  5. Nicole says:

    Nice tutorial. Do you ahve a site with others?

  6. Sina Salek says:

    Hi,
    Very nice function Marijan, i just tested it and it works find.
    Thanks for sharing it.

Leave a Reply


 *


 *


logo
logo
Powered by Wordpress | Designed by Elegant Themes | CopyRight ©2010 php4every1.com