How to find a string in all tables and all columns? - mysql

I'd like to write a query to find a string wherever it exists.
Something that would work like:
foreach(table in database) {
foreach(column in table) {
// in the end, i need to know, which columns in
// which tables that string appears.
}
}
Is this possible?

May I ask why? Honestly, unless it is something you need to do at runtime, I would use mysqldump and use a text editor to do the search.
If you have to do this at runtime, you are going to have to build something dynamically. You can use "show tables" to get a list of tables. You can then use show columns for each of those tables. You'd then need to do some sort of select statement on each column looking for your text (using locate, or using like for example).
This is going to be a really slow process to run at real-time on a server...

yes, this is possible. But I don't think you can do it in pure SQL; you'd better do it in some script languages, like PHP, or shell scripts.

I dare to say, this is impossible with (current) MySQL, as metadata (such as column names) and data (such as the field values) cannot be part of the same query.
Especially
SELECT Field from (DESCRIBE <tablename>)
will error out, as will
SELECT Field from (SHOW FIELDS FROM <tablename>)
as will
DECLARE flds CURSOR FOR DESCRIBE <tablename>
in a stored procedure.
This is to say it is impossible INSIDE MYSQL - it is trivial in PHP and freinds.

Related

Save MySql 'Show' result in db

So I'm kind of stumped.
I have a MySql project that involves a database table that is being manipulated and altered by scripts on a regular basis. This isn't so unusual, but I need to automate a script to run (after hours, when changes aren't happening) that would save the result of the following:
SHOW CREATE TABLE [table-name];
This command generates the ready-to-run script that would create the (empty) table in it's current state.
In SqlWorkbench and Navicat it displays the result of this SHOW command in a field in a result set, as if it was the result of a SELECT statement.
Ideally, I want to take into a variable in a procedure, and change the table name; adding a '-mm-dd-yyyy' to end of it, so I could show the day-to-day changes in the table schema on an active server.
However, I can't seem to be able to do that. Unlike a Select result set, I can't use it like that. I can't get it in a variable, or save it to a temporary, or physical table or anything. I even tried to return this as a value in a function, from which I got the error that a function cannot return a result set - which explains why it's displayed like one in the db clients.
I suspect that this is a security thing in MySql? If so, I can totally understand why and see the dangers exposed to a hacker, but this isn't a public-facing box at all, and I have full root/admin access to it. Hopefully somebody has already tackled this problem before.
This is on MySql 8, btw.
[Edit] After my first initial comments, I need to add; I'm not concerned about the data with this question whatsoever, but rather just these schema changes.
What I'd really -like- to do is this:
SELECT `Create Table` FROM ( SHOW CREATE TABLE carts )
But this seems to be mixing apples and oranges, as SHOW and SELECT aren't created equal, although they both seem to return the same sort of object
You cannot do it in the MySQL stored procedure language.
https://dev.mysql.com/doc/refman/8.0/en/show.html says:
Many MySQL APIs (such as PHP) enable you to treat the result returned from a SHOW statement as you would a result set from a SELECT; see Chapter 29, Connectors and APIs, or your API documentation for more information. In addition, you can work in SQL with results from queries on tables in the INFORMATION_SCHEMA database, which you cannot easily do with results from SHOW statements. See Chapter 26, INFORMATION_SCHEMA Tables.
What is absent from this paragraph is any mention of treating the results of SHOW commands like the results of SELECT queries in other contexts. There is no support for setting a variable to the result of a SHOW command, or using INTO, or running SHOW in a subquery.
So you can capture the result returned by a SHOW command in a client programming language (Java, Python, PHP, etc.), and I suggest you do this.
In theory, all the information used by SHOW CREATE TABLE is accessible in the INFORMATION_SCHEMA tables (mostly TABLES and COLUMNS), but formatting a complete CREATE TABLE statement is a non-trivial exercise, and I wouldn't attempt it. For one thing, there are new features in every release of MySQL, e.g. new data types and table options, etc. So even if you could come up with the right query to produce this output, in a couple of years it would be out of date and it would be a thankless code maintenance chore to update it.
The closest solution I can think of, in pure MySQL, is to regularly clone the table structure (no data), like so:
CREATE TABLE backup_20220618 LIKE my_table;
As far as I know, to get your hands on the full explicit CREATE TABLE statement, as a string, would require the use of an external tool like mysqldump which was designed specifically for that purpose.

using INFORMATION_SCHEMA.COLUMNS in a TRIGGER to log changes to _all_ columns?

TRIGGERs can be used to log changes to individual DB columns as described at https://stackoverflow.com/a/779250/569976 but that technique requires you have an IF statement for each column. It's not a huge issue if you're just interested in changes to one column BUT if you're interested in changes to all columns it becomes a bit more unweildy.
I can get all the column names of a table, dynamically, by querying the INFORMATION_SCHEMA.COLUMNS table. My question is... can I use that to dynamically reference the column names? Like in the TRIGGER you'd do OLD.columnName <> NEW.columnName but I don't think you can really make a column name dynamic like that.
In PHP you could use variable variables. eg. $obj->$var. But if MySQL has anything remotely similar that'd be news to me.
Any ideas? Or am I just going to go with the old fashioned approach of writing an IF statement for each of the 100s of columns this table has?
The trigger can only reference identifiers directly. You can't use a variable or an expression to name an identifier.
That would require dynamic SQL with PREPARE and EXECUTE so you could have the statement parsed at runtime from a string, but you can't PREPARE a new statement inside a trigger, because the trigger is already executing in the context of the currently executing statement.
The simplest solution is to write a trigger that references each column directly, with as many IF statements as there are columns in the table (I wonder why you have hundreds of columns in your table; that sounds like a different problem of bad design).
The comments above mention a binary log parser. Debezium is an example of an open-source binlog parser.
MySQL also supports an audit plugin architecture, but frankly the existing implementations of audit plugins are pretty clumsy.
https://www.mysql.com/products/enterprise/audit.html
https://mariadb.com/resources/blog/introducing-the-mariadb-audit-plugin/
https://github.com/mcafee/mysql-audit

mysql - look through known databases?

I have a list of known database name that I would like to apply the same sql command.
It is as simple as:
use my_database;
inset into table (col)
values ('value');
I would like to use a for loop or some sort to replace the "use my_database" part.
I have googled but most answer as about "iterate through all databases", not a portion of known databases.
Please advise.
You don't actually need to use the use database command to interact with a specific schema. You can prefix table names as part of the insert query:
insert into schema_name.table_name (col) values ('value'); will produce the same end result.
If you're dealing solely with MySQL what you're trying to do may prove a bit cumbersome, although you could loop through all the desired schema/table combinations with a stored procedure (although if this is only a one time thing, it may be a bit overkill)
If you are interacting with MySQL from an application with write privileges, you could:
Build and array with schema names
The specifics would be language dependant but for example with PHP you'd do something like this:
$schemas = array('schema_one', 'schema_two', 'schema_three');
Loop through the schema array
Again, in PHP you'd use something like foreach as in:
foreach($schemas as $s)
{
$stmt = "insert into ".$s.".table_name (col) values('".$value."')";
// Here, you'd actually run $stmt against MySQL to run the insert.
}
This is an extremely rough example, I used PHP because that's where I spend 99% of my coding time, but you could use any other programming language to achieve the same thing

Convert text box values in access query to mysql

I am currently in the process of moving all my Access databases to a MySQL server. I have some pretty big queries I would like to convert into sql direct.
The only thing is that in those queries I am using the content of a textbox in my form :
IIf(IsNull([Formulaires]![DialogueMAJDossier]![FiltreTypeEntree]),[TypeDossier],[Formulaires]![DialogueMAJDossier]![FiltreTypeEntree])
(Excuse me for all the names being in french)
I know that when I convert it to MySQL syntax, it should give something like this :
IFNULL(`Formulaires`.`DialogueMAJDossier`.`FiltreTypeEntree`, `TypeDossier`)
But I have no idea how to account for the text box value in my query.
Any help will be gladly appreciated
Pass-Through queries cannot have parameters, so you'll have to use a workaround.
Option 1:
Save the SQL with "variables" in a template table, e.g. SELECT foo, {FiltreTypeEntree} FROM bar.
Then before executing the Pass-Through query, read the template SQL, Replace() the variable with the result of your IIf expression, and set the .SQL property of the query with the final string.
Option 2:
Create a "Variables" table in MySql. Fill its fields via code, and have your Pass-Through query join this table to get the variable values.
In a multi-user scenario, you'd have to introduce some kind of session management for Option 2, so I'd go with (1) in this case.

MySQL: Dump a database from a SQL query

I'm writing a test framework in which I need to capture a MySQL database state (table structure, contents etc.).
I need this to implement a check that the state was not changed after certain operations. (Autoincrement values may be allowed to change, but I think I'll be able to handle this.)
The dump should preferably be in a human-readable format (preferably an SQL code, like mysqldump does).
I wish to limit my test framework to use a MySQL connection only. To capture the state it should not call mysqldump or access filesystem (like copy *.frm files or do SELECT INTO a file, pipes are fine though).
As this would be test-only code, I'm not concerned by the performance. I do need reliable behavior though.
What is the best way to implement the functionality I need?
I guess I should base my code on some of the existing open-source backup tools... Which is the best one to look at?
Update: I'm not specifying the language I write this in (no, that's not PHP), as I don't think I would be able to reuse code as is — my case is rather special (for practical purposes, lets assume MySQL C API). Code would be run on Linux.
Given your requirements, I think you are left with (pseudo-code + SQL)
tables = mysql_fetch "SHOW TABLES"
foreach table in tables
create = mysql_fetch "SHOW CREATE TABLE table"
print create
rows = mysql_fetch "SELECT * FROM table"
foreach row in rows
// or could use VALUES (v1, v2, ...), (v1, v2, ...), .... syntax (maybe preferable for smaller tables)
insert = "INSERT (fiedl1, field2, field2, etc) VALUES (value1, value2, value3, etc)"
print insert
Basically, fetch the list of all tables, then walk each table and generate INSERT statements for each row by hand (most apis have a simple way to fetch the list of column names, otherwise you can fall back to calling DESC TABLE).
SHOW CREATE TABLE is done for you, but I'm fairly certain there's nothing analogous to do SHOW INSERT ROWS.
And of course, instead of printing the dump you could do whatever you want with it.
If you don't want to use command line tools, in other words you want to do it completely within say php or whatever language you are using then why don't you iterate over the tables using SQL itself. for example to check the table structure one simple technique would be to capture a snapsot of the table structure with SHOW CREATE TABLE table_name, store the result and then later make the call again and compare the results.
Have you looked at the source code for mysqldump? I am sure most of what you want would be contained within that.
DC
Unless you build the export yourself, I don't think there is a simple solution to export and verify the data. If you do it table per table, LOAD DATA INFILE and SELECT ... INTO OUTFILE may be helpful.
I find it easier to rebuild the database for every test. At least, I can know the exact state of the data. Of course, it takes more time to run those tests, but it's a good incentive to abstract away the operations and write less tests that depend on the database.
An other alternative I use on some projects where the design does not allow such a good division, using InnoDB or some other transactional database engine works well. As long as you keep track of your transactions, or disable them during the test, you can simply start a transaction in setUp() and rollback in tearDown().