Laravel 5: How to dump SQL query? - mysql

Laravel 5's built-in solution
In Laravel 5+, we can use \DB::getQueryLog() to retrieve all executed queries. Since, query logging is an extensive operation and cause performance issues so it's disabled by default in L5 and only recommend for development environments only. We can enable the query logging by using the method \DB::enableQueryLog(), as mentioned in [Laravel's documentation][1].
Problem in built-in solution
The DB::getQueryLog() function is great but sometimes we wish that it would be great if we get dump in flat SQL format, so we can copy/past it in our favorite MySQL application like phpMyAdmin or Sqlyog to execute it and debug or optimize it.
So, I need a helper function that helps me to produce dump with following additional info:
On which file and line number the dump has called.
Remove back-ticks from the query.
Flat query, so don't need to update binding parameters manually and I can copy/past SQL in phpMyAdmin etc to debug/optimize the query.

Custom Solution
Step 1: Enable Query Logging
Copy/past following block of code on top of route file:
# File: app/Http/routes.php
if (\App::environment( 'local' )) {
\DB::enableQueryLog();
}
Step 2: Add helper function
if (!function_exists( 'dump_query' )) {
function dump_query( $last_query_only=true, $remove_back_ticks=true ) {
// location and line
$caller = debug_backtrace( DEBUG_BACKTRACE_IGNORE_ARGS, 1 );
$info = count( $caller ) ? sprintf( "%s (%d)", $caller[0]['file'], $caller[0]['line'] ) : "*** Unable to parse location info. ***";
// log of executed queries
$logs = DB::getQueryLog();
if ( empty($logs) || !is_array($logs) ) {
$logs = "No SQL query found. *** Make sure you have enabled DB::enableQueryLog() ***";
} else {
$logs = $last_query_only ? array_pop($logs) : $logs;
}
// flatten bindings
if (isset( $logs['query'] ) ) {
$logs['query'] = $remove_back_ticks ? preg_replace( "/`/", "", $logs['query'] ) : $logs['query'];
// updating bindings
$bindings = $logs['bindings'];
if ( !empty($bindings) ) {
$logs['query'] = preg_replace_callback('/\?/', function ( $match ) use (&$bindings) {
return "'". array_shift($bindings) . "'";
}, $logs['query']);
}
}
else foreach($logs as &$log) {
$log['query'] = $remove_back_ticks ? preg_replace( "/`/", "", $log['query'] ) : $log['query'];
// updating bindings
$bindings = $log['bindings'];
if (!empty( $bindings )) {
$log['query'] = preg_replace_callback(
'/\?/', function ( $match ) use ( &$bindings ) {
return "'" . array_shift( $bindings ) . "'";
}, $log['query']
);
}
}
// output
$output = ["*FILE*" => $info,
'*SQL*' => $logs
];
dump( $output );
}
}
How to use?
Take dump of last executed query, use just after the query execution:
dump_query();
Take dump of all executed queries use:
dump_query( false );

On which file and line number the dump has
called.
I don't understand why you need this because you always know where you called the dump function but never mind you have your solution for that.
Remove back-ticks from the query.
You don't need to remove back-ticks as the query will work in MySQL along with them also.
Flat query, so don't need to update binding parameters manually and I can copy/past SQL in phpMyAdmin etc to debug/optimize the query.
You can use vsprintf for binding parameters as:
$queries = DB::getQueryLog();
foreach ($queries as $key => $query) {
$queries[$key]['query'] = vsprintf(str_replace('?', '\'%s\'', $query['query']), $query['bindings']);
}
return $queries;
And I would suggest you to checkout this github repo squareboat/sql-doctor

I was looking for simple solution and the one below worked for me.
DB::enableQueryLog();
User::find(1); //Any Eloquent query
// and then you can get query log
dd(DB::getQueryLog());
Reference Links:
How to Get the Query Executed in Laravel 5? DB::getQueryLog() Returning Empty Array
https://www.codegrepper.com/code-examples/php/dump+sql+query+laravel

Add this code in the top of your routes file.
Laravel 5.2 routes.php
Laravel 5.3+ web.php
<?php
// Display all SQL executed in Eloquent
Event::listen('Illuminate\Database\Events\QueryExecuted', function ($query) {
var_dump($query->sql);
var_dump($query->bindings);
var_dump($query->time);
echo "<br><br><br>";
});

For a Laravel 8 application it could be useful to put the following in the AppServiceProvider.php file:
/**
* Bootstrap any application services.
*
* #return void
*/
public function boot()
{
// [...]
// Dump SQL queries on demand **ONLY IN DEV**
if (env('APP_ENV') === 'local') {
DB::enableQueryLog();
Event::listen(RequestHandled::class, function ($event) {
if ( $event->request->has('sql-debug') ) {
$queries = DB::getQueryLog();
Log::debug($queries);
dump($queries);
}
});
}
// [...]
}
Then appending &sql-debug=1 to the url will dump the queries.

Related

Does get_post_meta() makes a separate query to the database?

A question of mine which I had to ask a long time ago.. I am curious if these wordpress functions like get_post_meta makes a sql query to the database or does it is loaded in WP_Query global variable when page is loaded? Thank you
get_post_meta() is a wrapper for get_metadata() and get_metadata() uses the global WP_Object_Cache object.
The relevant code is:
function get_metadata( $meta_type, $object_id, $meta_key = '', $single = false ) {
...
$meta_cache = wp_cache_get( $object_id, $meta_type . '_meta' );
if ( ! $meta_cache ) {
$meta_cache = update_meta_cache( $meta_type, array( $object_id ) );
if ( isset( $meta_cache[ $object_id ] ) ) {
$meta_cache = $meta_cache[ $object_id ];
} else {
$meta_cache = null;
}
}
...
}
where wp_cache_get() is checking the global WP_Object_Cache object $wp_object_cache and update_meta_cache() is updating the global WP_Object_Cache object $wp_object_cache if the data is not in the cache. Of course this update requires a SQL query.
Incidentally, the global WP_Object_Cache object $wp_object_cache is used for much more than post meta data - it is a generic cache and WordPress and plugins uses it for caching values that are expensive to recompute.

jQuery plugin query mySQL database for data

OK, my apologies for this 'simple' question but I've forgotten ....
Have a jQuery plugin where the data comes from a mySQL db. How do I pass the object via the plugin options - see said simple :)
e.g.
jQuery('elementName').pluginName({
elements_per_page:8,
data: // A GET function to the mySQL for the data
})
the php query is simple like this:
$sql = 'SELECT * FROM tableName';
$stmt = $dbh->prepare($sql);
$stmt->execute();
$row = $stmt->fetchAll(PDO::FETCH_OBJ);
$data = json_encode($row);
echo $_GET['callback'] . '(' . $data . ')';
I have just totally forgotten how to pass the object directly into the plugin. Oviously there is something like this but 'how':
jQuery.getJSON( "functions.php&callback=?",function(data){
return data;
})
Plugin options a bit like this
var settings = $.extend({
elements_per_page:5,
data:data,
}, options );
Thanks in advance
//// EDIT ////
If I do it by calling the data first e.g.:
jQuery.getJSON("functions.php?table=table",function(data){
return pluginTest(data)
})
AND THEN call the plugin from the function pluginTest(data) like this
function pluginTest(data){
jQuery('elementName').pluginName({
elements_per_page:8,
data:data
})
}
Obviously works (well it does :) ) BUT what I really want/would like to do is call the data from within the plugin NOT call the plugin from the data as per this edit so it is more like this
jQuery('elementName').pluginName({
elements_per_page:8,
data: // A GET function to the mySQL for the data
})`
//// END EDIT ////

Symfony2 execute SQL file in Doctrine Fixtures Load

I'm migrating an old web app based on SQL Server and ASP to Symfony2 and MySQL. I made some queries and export old data to individual SQL files.
How can I execute thoses files in my fixtures, when I run the command
$php app/console doctrine:fixtures:load
Now I have some fixtures that works directly with Doctrine ORM and entities, but I have a lot of data to import.
I find a good solution. I didn't find an exec method in class ObjectManager, so... this work very well for me.
public function load(ObjectManager $manager)
{
// Bundle to manage file and directories
$finder = new Finder();
$finder->in('web/sql');
$finder->name('categories.sql');
foreach( $finder as $file ){
$content = $file->getContents();
$stmt = $this->container->get('doctrine.orm.entity_manager')->getConnection()->prepare($content);
$stmt->execute();
}
}
In this solution your fixture class has to implement the ContainerAwareInterface with the method
public function setContainer( ContainerInterface $container = null )
{
$this->container = $container;
}
You can load the file contents as a string, and execute native SQL using the EntityManager:
class SQLFixtures extends AbstractFixture implements OrderedFixtureInterface
{
$filename = '/path/to/sql/file.sql';
public function load(ObjectManager $manager) {
$sql = file_get_contents($filename); // Read file contents
$manager->getConnection()->exec($sql); // Execute native SQL
$manager->flush();
}
public function getOrder() {
return 99; // Order in which this fixture will be executed
}
}
Answer for Zend Framework 2.5.3 using Doctrine Data-Fixtures.
Not sure if this applies to the given answers, but they are trying a bit too hard. If you inspect the given $manager object, you'll find that it already is the EntityManager (of interface ObjectManager) (at least, in ZF2). As such you're able to get the Connection directly and it's possible to execute without using $this->container->get('doctrine.orm.entity_manager')
Below a snippet which I use for creating the first user "system", with a createdBy FK reference to itself.
public function load(ObjectManager $manager)
{
$sql = 'INSERT INTO users (
id, username, email, display_name, `password`, created_by)
VALUES (:id, :username, :email, :display_name, :password, :created_by)';
$password = $this->createSuperDuperEncryptedPassword();
// $manager === `EntityManager|ObjectManager`, `->getConnection()` is available
$stmt = $manager->getConnection()->prepare($sql);
$stmt->bindValue(':id', 1);
$stmt->bindValue(':username', 'system');
$stmt->bindValue(':email', 'system#system.test');
$stmt->bindValue(':display_name', 'system');
$stmt->bindValue(':password', password );
$stmt->bindValue(':created_by', 1); // Self reference
$stmt->execute();
}

MediaWiki DB connection error while attempting to upgrade to 1.22

I have a MediaWiki installation on a shared host server. It's at version 1.19.1 and I'm trying to update to 1.22.2. The documentation indicates that a one-step update should be OK for this.
I've done this several times for past updates successfully, and am following previous notes. I set up a new directory with 1.22.2 in it, copied LocalSettings.php and /images/ files from the working live directory to the new one. LocalSettings.php has entries for $wgDBuser, $wgDBpassword, $wgDBadminuser and $wgDBadminpassword all defined.
I have command line access to the server, and tried to run the update process in WikiNew, by
php maintenance/update.php
but it responds:
DB connection error: Unknown MySQL server host 'localhost:/tmp/mysql5.sock' (34) (localhost:/tmp/mysql5.sock)
If I do the same in WikiLive it works. Of course it does not do any actual update as I'm updating 1.19.1 to 1.19.1, but the usual type of messages appear but with indications that changes are not required, and it purges caches. LiviWiki, 1.19, still works.
So the same data for the connection string exists in both copies of LocalSettings.php, both copies of maintenance/update.php are accessing the same MySQL database, but one accepts the connection string and the other doesn't.
Has something changed between 1.19 and 1.22? I've looked for 'Configuration changes' in the release notes for 1.20, 1.21, and 1.22, but see no instruction to make any change.
Please help!
Thank you.
For the record, the answer was to change the DB host from
$wgDBserver = "localhost:/tmp/mysql5.sock"
to just
$wgDBserver = "localhost"
The original string should have worked, but there is a bug in MediaWiki 1.19.2, described here:
https://bugzilla.wikimedia.org/show_bug.cgi?id=58153
"The new mysqli adapter in 1.22.0 does not properly implement non-standard MySQL
ports."
I ran into a similar problem with the difference that the connect string for the MySQL server I use was of the form of host:port:socket as in localhost:3306:/var/lib/mysock.
I am attempting to install mediawiki-1.22.6 and the initial database check was failing. Using only localhost did not work for me since the MySQL installation I am using required both a port number and a socket.
I ended up making the following changes to the mediawiki-1.22.6 php scripts in order to allow for the parsing of a connect string of the form of host:port:socket.
WARNING: These changes are specific to my site and environment and the parsing function changes probably will not work properly with other strings for instance if the port number is not specified as in localhost:/var/lib/mysock.
Here are the specific changes I made to complete installation.
In file IP.php in the relative directory of Includes there is a function public static function splitHostAndPort( $both ) which takes the connect string and parses it breaking it up into the necessary parts for the real_connect() call in function protected function mysqlConnect( $realServer ) located in file DatabaseMysqli.php in the relative folder includes/db.
in the function splitHostAndPort() I modified the function to the following:
public static function splitHostAndPort( $both ) {
if ( substr( $both, 0, 1 ) === '[' ) {
if ( preg_match( '/^\[(' . RE_IPV6_ADD . ')\](?::(?P<port>\d+))?$/', $both, $m ) ) {
if ( isset( $m['port'] ) ) {
return array( $m[1], intval( $m['port'] ) );
} else {
return array( $m[1], false );
}
} else {
// Square bracket found but no IPv6
return false;
}
}
$numColons = substr_count( $both, ':' );
if ( $numColons >= 2 ) {
// Is it a bare IPv6 address?
if ( preg_match( '/^' . RE_IPV6_ADD . '$/', $both ) ) {
return array( $both, false );
} else {
// Not valid IPv6, but too many colons for anything else
// may be of the form localhost:port:socket
// return false;
}
}
if ( $numColons >= 1 ) {
// Host:port:socket?
$bits = explode( ':', $both );
if ( preg_match( '/^\d+/', $bits[1] ) ) {
if ($numColons > 1) {
return array( $bits[0], intval( $bits[1] ), $bits[2] );
} else {
return array( $bits[0], intval( $bits[1] ) );
}
} else {
// Not a valid port
return false;
}
}
// Plain hostname
return array( $both, false );
}
Next I modified the mysqlConnect() function so that it performed a check to see if both port and socket are specified as follows:
// Other than mysql_connect, mysqli_real_connect expects an explicit port
// parameter. So we need to parse the port out of $realServer
$socketname = $port = null;
$hostAndPort = IP::splitHostAndPort( $realServer );
if ( $hostAndPort ) {
$realServer = $hostAndPort[0];
if ( $hostAndPort[1] ) {
$port = $hostAndPort[1];
}
if ( $hostAndPort[2] ) {
$socketname = $hostAndPort[2];
}
}
and then modified the call to real_connect() to also specify a socket
if ( $mysqli->real_connect( $realServer, $this->mUser,
$this->mPassword, $this->mDBname, $port, $socketname, $connFlags ) )

How to see MySQL statements and error (if any) in CakePHP shell

I am using CakePHP 1.3 and writing custom shells to run mundane tasks in cronjobs. I am seeing failed Model->save() from time to time but I don't know anyway to find out what the exact problem is.
Is there a way to display the actual SQL statements executed and warning/error returned by MySQL in a CakePHP shell?
Thanks.
You can use the following SQL dump task for shells.
http://bakery.cakephp.org/articles/carcus88/2011/04/08/sql_dump_task_for_shells
One way to do this would be to watch the MySQL log file in a separate terminal.
A couple ways of doing this are listed here:
MySQL Query Logging in CakePHP
I found a way to do it. In your shell, add:
function initialize()
{
Configure::write('debug', 2);
$this->_loadDbConfig();
$this->_loadModels();
}
Then whenever you like to see the log, call this function:
function dump_sql()
{
$sql_dump = '';
if (!class_exists('ConnectionManager') || Configure::read('debug') < 2)
return false;
$noLogs = !isset($logs);
if ($noLogs)
{
$sources = ConnectionManager::sourceList();
$logs = array();
foreach ($sources as $source):
$db =& ConnectionManager::getDataSource($source);
if (!$db->isInterfaceSupported('getLog')):
continue;
endif;
$logs[$source] = $db->getLog();
endforeach;
}
if ($noLogs || isset($_forced_from_dbo_))
{
foreach ($logs as $source => $logInfo)
{
$text = $logInfo['count'] > 1 ? 'queries' : 'query';
$sql_dump .= "cakeSqlLog_" . preg_replace('/[^A-Za-z0-9_]/', '_', uniqid(time(), true));
$sql_dump .= '('.$source.') '. $logInfo['count'] .' '.$text. ' took '.$logInfo['time'].' ms';
$sql_dump .= 'Nr Query Error Affected Num. rows Took (ms)';
foreach ($logInfo['log'] as $k => $i)
{
$sql_dump .= $i['query'];
}
}
}
else
{
$sql_dump .= 'Encountered unexpected $logs cannot generate SQL log';
}
}
One other approach would be to have all your custom queries in the models/behaviors, and just calling the data/updates from shells. This would give you an extra benefit of being able to reuse those custom SQL in other parts of the project. For example, in unit tests.
In CakePHP 1.2, I was able to get the SQL queries to show up in my console output by adding a Configure::write('debug', 2); call to the bottom of the __bootstrap method in the cake/console/cake.php file.
No need to mess around with specifically calling a dump_sql function like some of these answers, I just automatically get the normal queries like at the bottom of a web page.