MYSQLi search and replace with a quoted font name - mysql

When building my web site I initially used the font Verdana quite a bit. I now wish to go through the mysql database and change all instances of :
font-family: Verdana;
to
font-family: 'Open Sans', sans-serif;
BUT when I initially tried a search and replace function within the phpMyAdmin panel the system balked over the quoted 'Open Sans'. I had a similar response when I entered a mysql command in the Run SQL query/queries window.
I have looked around the StackOverflow site and I saw some references to possibly using a \ in front of the quotes, but it didn't seem to actually apply in the situation I am trying to do a search and replace.
Any hints on what the mysql command needs to be in this type of situation..
Thanks in advance

Write it as a SELECT statement first. We can use the MySQL REPLACE function. https://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_replace
e.g.
SELECT t.mycol AS oldval
, REPLACE(t.mycol,'font-family: Verdana;','font-family: ''Open Sans'', sans-serif;') AS newval
-- ^ ^^ ^^ ^
FROM mytable t
WHERE t.mycol LIKE '%font-family: Verdana;%'
Note that single quote characters in a string literal can be specified by two single quote characters.
As a demonstration, consider:
SELECT 'It''s good' AS foo
-- ^ ^^ ^
Once we have a tested and verified expression for newval, we can use that expression in an UPDATE statement
UPDATE mytable t
SET t.mycol = REPLACE(t.mycol,'font-family: Verdana;','font-family: ''Open Sans'', sans-serif;')
WHERE t.mycol LIKE '%font-family: Verdana;%'
Note that this will replace only the first occurrence of the string in mycol. A subsequent execution will find the next occurrence of the search string, and replace that.
Also note that REPLACE performs a case-sensitive match.

Related

'parentheses not balanced' regexp error continues to occur - I cannot identify the pattern of failure

Originally I thought this problem was a general failure of my understanding or could be generecized to a case that would be useful to others. I still haven't solved the problem, but upon learning more about the boundaries of the problem I see it is probably something highly specific and not of use to the community. Thank you for any and all help and time taken to help.
Summary
When I pass the value (Unit-101|00-102|Unit_103|Unit 104) to my stored procedure (as a VARCHAR, to use as comparison in RLIKE in a WHERE clause) - it will generate the error 'parentheses not balanced'. However, the stored procedure works perfectly when other values are passed in - three or less in the capture group (e.g. (Unit-101|00-102|Unit_103)), wildcard (.*) or wildcard-component (Unit-1.*) values work perfectly. Also, a very similar stored procedure works perfectly when more than three values are in the capture group... (Please see below for more details. Thank you.)
Edit: At least that's what I thought. I tried (a|b|c|d) (I've tried other values before which did not work) - and it worked. So I'm once again at a complete loss.
Problem Context
I have a stored procedure which includes some regex in a WHERE clause - it aggregates some totals for entries only where a Unit name matches the regex passed in to the procedure (_r_unit, an IN VARCHAR(100)).
This has worked as expected for many cases of _r_unit - .*, Unit-B.*, Unit-101, etc. - however in one case, when I used parentheses to capture Unit-101, or 00-102, or Unit_103, or Unit 104 - (Unit-101|00-102|Unit_103|Unit 104) - then the query fails with the error #1139 - Got error 'parentheses not balanced' from regexp.
Steps attempted to find solution so far
I first discovered this problem while passing in the regexp value from php, using preg_quote to escape the - characters in the unit names. However, a commenter helpfully pointed out that the - character should not need to be escaped here, and php will mean it needs to be 'double-escaped' anyway. So, I have tried some php-related things, but this does not appear to be the issue - now that I've eliminated that as the cause I'm just passing in values by hand using phpmyadmin to examine the conditions which cause an error. For reference, the variable which was passed in as a value to the stored procedure from php was set to -
"(" . preg_quote("Unit-101") . "|" . preg_quote("00-102") . "|"
. preg_quote("Unit_103") . "|" . preg_quote("Unit 104") . ")";
The literal contents of the variable (examined by echoing it out) was (Unit\-101|00\-102|Unit_103|Unit 104), as expected.
I have also examined the regex in regex101 to check it 'does what it says on the tin' - it's as expected, looking for Unit-101, 00-102, Unit_103, or Unit 104.
Variations on regex input I have tried.
Regex which does not generate the error
CALL StoredProcedure('(Unit-101)'); // Or any other of the four units, non-escaped dash
CALL StoredProcedure('(Unit\-101)'); // Or any other of the four units, escaped dash
CALL StoredProcedure('(Unit-101|00-102)')
CALL StoredProcedure('(Unit_103|Unit 104)')
CALL StoredProcedure('(Unit-101|Unit_103|Unit 104)')
CALL StoredProcedure('(00-102|Unit_103|Unit 104)')
CALL StoredProcedure('(Unit-101|00-102|Unit 104)')
Regex which generates the error
CALL StoredProcedure('(Unit-101|00-102|Unit_103|Unit 104)')
CALL StoredProcedure('(Unit 104|00-102|Unit-101|Unit_103)')
CALL StoredProcedure('(Unit-101|00-102)|(Unit_103|Unit 104)')
I tried this with some random values too - and it seems to break with 4 values, regardless of what they are.
I just tried running another stored procedure which takes in the same value (i.e. _r_unit) and it works correctly with 4 values. They are very similar queries so I'm trying to find a difference in the WHERE clauses where the regex is used but I can't find any ...
Stored Procedure 1 WHERE clauses - Generates Error
/* first WHERE clause, to retrieve results FROM one database */
WHERE
`Date` BETWEEN _startDate AND _endDate
AND `Unit Type` = _unitType
AND `Unit Serial` RLIKE _r_unit
AND `Driver` RLIKE _r_driver
AND `Error` != ""
AND `Error` RLIKE _r_errorCode
/* second WHERE clause, to retrieve results FROM a second (relational) database */
/* (there are two WHERE clauses because the results are unioned together) */
WHERE
DB._Records.Date BETWEEN _startDate AND _endDate
AND DB.UnitTypes.Module = _unitType
AND DB.Units.Serial RLIKE _r_unit
AND DB.Drivers.Driver RLIKE _r_driver
AND DB.ErrorCodes.ErrorCode != ""
AND DB.ErrorCodes.ErrorCode RLIKE _r_maintCode
Stored Procedure 2 WHERE clauses - Does not generate error
/* first WHERE clause, to retrieve results FROM one database */
WHERE
`Date` BETWEEN _startDate AND _endDate
AND `Unit Type` = _unitType
AND `Unit Serial` RLIKE _r_unit
AND `Driver` RLIKE _r_driver
/* second WHERE clause, to retrieve results FROM a second (relational) database */
/* (there are two WHERE clauses because the results are unioned together) */
WHERE
DB._Records.Date BETWEEN _startDate AND _endDate
AND DB.UnitTypes.Module = _unitType
AND DB.Units.Serial RLIKE _r_unit
AND DB.Drivers.Driver RLIKE _r_driver
(I am aware some fields are badly named however this is an old system and a lot of things are dependent on it so they would be difficult to change)
I'm at my wit's end! Any advice much appreciated!
This answer does not address the issue exposed in the question but it is too long to fit in a comment.
A small quote from MySQL RLIKE documentation:
Because MySQL uses the C escape syntax in strings (for example, ā€œ\nā€ to represent the newline character), you must double any ā€œ\ā€ that you use in your REGEXP strings.
...
To use a literal instance of a special character in a regular expression, precede it by two backslash (\) characters. The MySQL parser interprets one of the backslashes, and the regular expression library interprets the other.
This means the backslashes (\) are swallowed by MySQL and the regular expression engine does not see them. However, in the exposed regex, the dashes (-) have no special meaning and it's OK to leave them unquoted.
Unfortunately, this does not explain why your query fails with that strange error message.
Hope you already solved your problem! In case not, here is how I fixed the problem of a query working perfectly at the MySQLprompt but giving the 'regexp brackets not balanced' MySQL error when running it through PHP.
The simple solution is that one needs to add one extra backslash when going through PHP!
So, as an example, to match any of {[( MySQL will be happy with REGEXP [{\\[(] but the same query executed via php needs REGEXP [{\\\[(]
Hope this helps!

MySQL for replace with wildcard

I'm trying to write a SQL update to replace a specific xml node with a new string:
UPDATE table
SET Configuration = REPLACE(Configuration,
"<tag>%%ANY_VALUE%%</tag>"
"<tag>NEW_DATA</tag>");
So that
<root><tag>SDADAS</tag></root>
becomes
<root><tag>NEW_DATA</tag></root>
Is there a syntax im missing for this type of request?
Update: MySQL 8.0 has a function REGEX_REPLACE().
Below is my answer from 2014, which still applies to any version of MySQL before 8.0:
REPLACE() does not have any support for wildcards, patterns, regular expressions, etc. REPLACE() only replaces one constant string for another constant string.
You could try something complex, to pick out the leading part of the string and the trailing part of the string:
UPDATE table
SET Configuration = CONCAT(
SUBSTR(Configuration, 1, LOCATE('<tag>', Configuration)+4),
NEW_DATA,
SUBSTR(Configuration, LOCATE('</tag>', Configuration)
)
But this doesn't work for cases when you have multiple occurrences of <tag>.
You may have to fetch the row back into an application, perform string replacement using your favorite language, and post the row back. In other words, a three-step process for each row.

Change part of record with regular expression in mysql

I work on my site in localhost, I have a field in my database which I store absolute url of some pictures, so all records are like http://localhost/my_project/images/picture.jpg and now in my website this records not work, I should change all records to like this 'http://www.mysite.com/images/picture.jpg', so I found replace command for mysql but this command will replace all part of field:
UPDATE tablename SET tablefield = replace(tablefield, "findstring", "replacestring");
How can I change just http://localhost/ in my records with phpmyadmin ?
First, you should remove the domain name from all of your url's. Simple using /images/picture.jpg will allow it to work on all hosts.
To replace it globally, I would recommend doing a mysqldump and opening the file in a text editor, replacing the strings, and importing it back into the database. That's assuming you don't have any serialized strings (wordpress) in your database.
To simply strip the 'http://localhost' prefix, in order to use host-less absolute paths as recommended in #Rob's answer:
UPDATE tablename
SET tablefield = SUBSTR(tablefield FROM 17)
WHERE tablefield LIKE 'http://localhost/%'
See it on sqlfiddle.
To replace with 'http://www.mysite.com' as asked in the original question:
UPDATE tablename
SET tablefield = CONCAT('http://www.mysite.com', SUBSTR(tablefield FROM 17))
WHERE tablefield LIKE 'http://localhost/%'
See it on sqlfiddle.
Of course, neither of these answers use regular expressions as requested in your question title (although they do use simple pattern matching); to use regular expression pattern replacement, you will need to install and use a UDF such as provided by lib_mysqludf_preg.

Access VBA Replace function returning values from Replace string

I am trying to use the REPLACE function to search a string and remove a charcter. Here is the code..
SELECT Test.*, Replace([Data],ChrW(10),"",(Len([Data])-2),1) FROM Test;
Although this is just a select stmt, the result set returns the last three characters of data from the column [Data]. Instead of starting the search in that location.
The UPDATE statement here, does the same...
UPDATE Test SET Test.Data = Replace([Data],ChrW(10),"",(LEN([Data])-2))
WHERE (((Test.[Data]) Like ("*" & ChrW(10))));
I would still expect the search to being at the results of LEN([Data])-2. Instead only the last characters are returned. The substitution is successful.
Any help on my oversight here would be appreciated.
Weird. I have confirmed what you're getting. In a query, adding a starting position to the Replace() function acts as a Right() function as well. I can't find any documentation for that. In VBA, it behaves as one would expect, in that it returns the whole string, sans the replaced chars.
You have 3 options for a workaround.
You can replace all Chrw(10), but I think you probably don't want to do that since you specified a start position.
You can use the IIF() statement to test if the last char is Chr(10), and if so, do a Left() function to exclude the last char. Kinda messy.
If you're working in Access, you can create your own function in VBA and call it instead. You can call your function Repl, so it would look something like this: UPDATE Test SET Test.Data = Repl([Data]).

mysql: replace \ (backslash) in strings

I am having the following problem:
I have a table T which has a column Name with names. The names have the following structure:
A\\B\C
You can create on yourself like this:
create table T ( Name varchar(10));
insert into T values ('A\\\\B\\C');
select * from T;
Now if I do this:
select Name from T where Name = 'A\\B\C';
That doesn't work, I need to escape the \ (backslash):
select Name from T where Name = 'A\\\\B\\C';
Fine.
But how do I do this automatically to a string Name?
Something like the following won't do it:
select replace('A\\B\C', '\\', '\\\\');
I get: A\\\BC
Any suggestions?
Many thanks in advance.
You have to use "verbatim string".After using that string your Replace function will
look like this
Replace(#"\", #"\\")
I hope it will help for you.
The literal A\\B\C must be coded as A\\\\A\\C, and the parameters of replace() need escaping too:
select 'A\\\\B\\C', replace('A\\\\B\\C', '\\', '\\\\');
output (see this running on SQLFiddle):
A\\B\C A\\\\B\\C
So there is little point in using replace. These two statements are equivalent:
select Name from T where Name = replace('A\\\\B\\C', '\\', '\\\\');
select Name from T where Name = 'A\\\\B\\C';
Usage of regular expression will solve your problem.
This below query will solve the given example.
1) S\\D\B
select * from T where Name REGEXP '[A-Z]\\\\\\\\[A-Z]\\\\[A-Z]$';
if incase the given example might have more then one char
2) D\\B\ACCC
select * from T where Name REGEXP '[A-Z]{1,5}\\\\\\\\[A-Z]{1,5}\\\\[A-Z]{1,5}$';
note: i have used 5 as the max occurrence of char considering the field size is 10 as its mentioned in the create table query.
We can still generalize it.If this still has not met your expectation feel free to ask for my help.
You're confusing what's IN the database with how you represent that data in SQL statements. When a string in the database contains a special character like \, you have to type \\ to represent that character, because \ is a special character in SQL syntax. You have to do this in INSERT statements, but you also have to do it in the parameters to the REPLACE function. There are never actually any double slashes in the data, they're just part of the UI.
Why do you think you need to double the slashes in the SQL expression? If you're typing queries, you should just double the slashes in your command line. If you're generating the query in a programming language, the best solution is to use prepared statements; the API will take care of proper encoding (prepared statements usually use a binary interface, which deals with the raw data). If, for some reason, you need to perform queries by constructing strings, the language should hopefully provide a function to escape the string. For instance, in PHP you would use mysqli_real_escape_string.
But you can't do it by SQL itself -- if you try to feed the non-escaped string to SQL, data is lost and it can't reconstruct it.
You could use LIKE:
SELECT NAME FROM T WHERE NAME LIKE '%\\\\%';
Not exactly sure by what you mean but, this should work.
select replace('A\\B\C', '\', '\\');
It's basically going to replace \ whereever encountered with \\ :)
Is this what you wanted?