Updating same query repeatedly in database generation - php

I have generated entities from a (MySQL) database, then created a new (zf2) project with a blank database by running the following statements:
vendor/bin/doctrine-module orm:schema-tool:create
vendor/bin/doctrine-module orm:schema-tool:update --force
vendor/bin/doctrine-module orm:validate-schema
Unfortunately the update statement keeps updating the same statement:
ALTER TABLE rollen CHANGE name name VARCHAR(30) NOT NULL;
and the validation fails. The DB shows the correct column. The Doctrine\DBAL\Schema\Comparator shows a different precision (not yet quite sure what that is supposed to be) of the corresponding column.
Some related questions did not seem to fit or work.

Looking at the corresponding diffColumn function in Doctrine\DBAL\Schema\Comparator, showed me that auto-increment set in the meta-data schema (see following annotation):
#ORM\GeneratedValue(strategy="IDENTITY")
the precision part seems to be part of this strategy (although I still don't understand this, since a auto-increment id is rather an in than decimal, see http://doctrine-orm.readthedocs.org/en/latest/reference/annotations-reference.html#column)

Related

Laravel PHPUnit failing on ALTER TABLE using SQLite

I have a migration which I made at the beginning of my project, basically adding a TEXT column called 'description' which is set to NOT NULL.
Now several months down the track I need to change that to allow null.
I can't use Laravel 5.5 change() function as I have a enum in my column list and it bugs out, so i need to add it as a raw query in a migration like so;
DB::statement('ALTER TABLE `galleries` MODIFY `description` TEXT NULL;');
When i do a php artisan migrate against my local mysql database it all works great, BUT when i try to run my test suite, it all breaks.
Im using SQLite for my test suite, and the error im getting is as follows;
PDOException: SQLSTATE[HY000]: General error: 1 near "MODIFY": syntax error
If anyone else has come up against this issue and fixed it, i would love to hear how you did it.
Thanks
SQLite only allows you to rename the table or add a column. The ALTER TABLE statement cannot change or remove columns.
In order to change or remove a column in SQLite, you need to create a new table with the desired schema, copy the data from the original table to the new table, delete the original table, and then rename the new table to the original name.
This is all abstracted out for you by Laravel and DBAL, so your best bet may be to get help with figuring out the issue with your enum column (though that would be a separate question).
You can read more about altering tables in the SQLite docs here.

Does redbean php consider created mysql-triggers?

I am using redbeanphp 4.3 and struggling to understand why a mysql-db-trigger does not get triggered (or may be ignored).
When I work directly in the database (with phpmyadmin) and insert a row in table B, a column in table A gets updated.
When I work indrectly with the database (via a rest-api and therefore with redbean php), the row is inserted as well in table B, but the column in table A does not get updated.
This exact same behaviour I found in my android application with sugarorm too.
I then reasoned, that using an ORM ignores the usage of triggers. But so far I did not find any statement support my thesis, not in some forums, the redbean documentation nor in the rd.php source code.
An explanation or any help would be appreciated.
Thanks in advance.
Edit 1:
A script using mysqli and pdo-driver does result in the same behaviour as manual insertion (the column gets updated). Therefore PDO can not be the reason.
Solution:
Table A and B have an 1:N relation.
The problem was a misuse of redbean's R::store() method. The object1 of table A was loaded, an object2 of table B was inserted (by R::store()), the trigger was executed, but the object1 of table A did not recognize it. A final R::store() on object1 has overwritten the background updates to the initial state.
So the solution is quite simple: removing R::store() on objects of table B.
When R::store() is called on object1 of table A, it first updates entity A and inserts entity B afterwards, which will trigger the update of object1.
Try to debug the sql querys created by redBean and check if there is a column update or something else after your insert.
Configure redBean to log sql querys:
R::debug( TRUE, 3 );
Access and print the log:
$logs = R::getDatabaseAdapter()->getDatabase()->getLogger()->getLogs();
print_r( $logs );
You will find more information about debugging redBean on their official site.

MySQL table from phpMyAdmin's tracking reports

Using phpMyAdmin I can track a certain table's transactions (new inserts, deletes, etc), but is it possible to change or export it to a SQL table to be imported into my site using PHP.
While it is not exactly what I was looking for, I found a way to do it for the time being, one of the tables in the phpmyadmin (it's own) database is called pma__tracking, which contains a record of all tables being tracked, one of its columns is the data_sql longtext column which writes each report (ascendingly which is a bit annoying) in the following format
# log date username
data definition statement
Just added it for future references.

Renaming a column in a migration script in Doctrine2

When I have a Table reference and I call renameColumn() on it, I get the following pair of errors:
Migration 20130725141653 failed during Execution. Error Table#renameColumn() was removed, because it drops and recreates the column instead. There is no fix available, because a schema diff cannot reliably detect if a column was renamed or one column was created and another one dropped.
[Doctrine\DBAL\DBALException]
Table#renameColumn() was removed, because it drops and recreates the column instead. There is no fix available, because a schema diff cannot reliably detect if a column was renamed or one column was created and another one dropped.
(executed via doctrine migrations:migrate --dry-run)
Which makes sense... for a diff program.
I'm writing a migration. I know that I want the column renamed (preserving data).
Is there any pure Doctrine method (I don't want to write a raw query) to acheive this?
The 'diff' that the exception is talking about is the diff between your original Schema and your updated Schema. This is how Doctrine generates the SQL statements -- by comparing the current Schema to the modified Schema.
There is no way for Doctrine to reliably figure out that someColumn in $originalTable is now someRenamedColumn in $modifiedTable (pardon the bad psuedocode) and so the functionality was removed .

Create Table Else Alter Table

This seems to be a simple problem, but after a while of searching I can't figure out the answer.
I currently have a MySQL table in my local database used by a webapp, and them same table on a database in a remote server. Right now, I'm using the CREATE TABLE IF NOT EXISTS command through PHP to create the table on the databases:
CREATE TABLE IF NOT EXISTS users (
`id` int(10) NOT NULL AUTO_INCREMENT,
`username` varchar(18) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=2 ;
However, let's say I make a modification to the local database, adding a collumn, for example. It would be really annoying to have to go and change the remote database every time I change the local one. Is there an easier way to run code to create a table if it doesn't exist, and if it does exist, make sure it's structure matches that of the create table structure?
Here's an example, to make what I'm trying to convey a little clearer. Let's say on the local database I have a users table, and I decide that in my webapp I want to have another collumn, password. So I go to the local database and add a password collumn. Is there PHP/MySQL code I can run to check if the users table exists, and if it does, make sure it has a password collumn, and if not, add it?
What you are actually looking for are Migrations, e.g. you are looking for a Schema Management Tool that lets you manage your Database structure in versioned code diffs.
For instance, for your described scenario you would first create a script to create the table, e.g. 001_create_user_table.sql. Then you'd use the schema manager to connect and deploy these changes to your databases.
When you want to change or add something, you just write another script, for instance, 002_Add_Password_Column_To_User_Table.sql. Fill in just the code to do that change. Then run the schema manager again.
Typically, you tell the Schema Manager to go through all existing migrations files. On each run, the Schema manager will update a changelog table in the database, so when you run it, it will know which of your scripts it should apply.
The good thing is, you can add these migrations to your regular VCS, so you will always know which database schema you had at which version of your application. And you will have a proper changelog for them.
To directly answer your question you can create temporary procedures to detect field existence like using a query like this:
SHOW COLUMNS FROM table_name LIKE 'column_name';
However in the real world, database changes are general rolled into three scripts. A create script and two deltas one up and one down. Then the database is versioned so that you know at what state the database is in at any given time.
To specifically check for a password column you can use DESCRIBE:
$colExists = false;
$res = mysql_query('DESCRIBE `users`');
while ($row = mysql_fetch_assoc($res)) {
if ($row['Field'] == 'password') {
$colExists = true;
break;
}
}
if (!$colExists) {
// create column
}
However, you should check into replication or some other automated tool to see if they would be a better solution for you.
Follow these steps (you can easily implement this in PHP, I assumed that the name of the table is Foo)
1.) Run the following code:
desc Foo
2.) Based on the result of the first step you can make your create table command (and you should)
3.) Store your data from the existing table which will be replaced in a variable (Optional, you only need this if you can potentially use data from the old table)
4.) Modify the extracted rows from step 3.) so they will be compatible with your new definition (Optional, you only need this if you can potentially use data from the old table)
5.) Get the rows from your new Foo table
6.) Merge the results got in steps 4.) an 5.) (Optional, you only need this if you can potentially use data from the old table)
7.) Run a drop table for the old table
8.) Generate a replace into command to insert all your rows into the newly created Foo table (you can read more about this here)
After these steps, as a result, you will have the new version of the table. If your tables are too large, you can do a CREATE TABLE IF NOT EXISTS command and if that was not successful, run the alter command.
Also, you can make a library to do these steps and will use that in the future instead of solving the same problem several times.
EDIT:
You can connect the database using this function: mysql-connect (documentation here)
You can run a query using this function: mysql-query (documentation here)
Based on the first step you will get the field names (let's assume you store it in a variable called $bar) and you can use your result to generate your select command (connecting to the database where you have important data. It may be both):
$field_list = "1";
foreach ($bar as $key => $value)
$field_list.= ",".$bar[$key];
mysql_connect(/*connection data*/);
mysql_query("select ".$field_list." from Foo");
You can use your new resource to build up an insert command to insert all your important data after deletion recreation (about resources read more here, about how you can generate your insert you can read here, but I suggest that you should use replace into instead of insert which works like the insert, except that it replaces the row if it already exists, it's better here than an insert, read more here)
So, use mysql_connect and mysql_query, and the resource returned by the mysql_query function can be used for replace into later (I've linked now the URL's for everything you need, so I'm pretty sure you'll solve the problem.), apologies for being not specific enough before.

Categories