Laravel data mismatch error while using \PDO::ATTR_EMULATE_PREPARES => true - laravel

We have application build in Php Laravel and for the database we use postgres sql. And also on top of postgres we have configure pgBouncer to limit the maximum number of connections on server side by managing a pool of idle connections that can be used by any applications.
Now, we face the issue with the boolean values (True(0),False(1)) used in the application (Php Laravel). It gives below error when any CRUD operation is performed. In the below error column "revoked" is boolean type.
column \"revoked\" is of type boolean but expression is of type integer
You will need to rewrite or cast the expression. (SQL: \"revoked\", \"created_at\") values (0, 2020-02-07 06:09:06)
Now after exploring, I came to know that boolean values needs to be consider to be string with the pgBouncer. So I have made changes in the connection.php file, located in "\vendor\laravel\framework\src\Illuminate\Database". I have change the code to consider the boolean value as mentioned below.
public function bindValues($statement, $bindings)
{
foreach ($bindings as $key => $value) {
//if(is_bool($value))
$statement->bindValue(
is_string($key) ? $key : $key + 1, $value,
//is_int($value) ? PDO::PARAM_INT : PDO::PARAM_STR
is_int($value) ? PDO::PARAM_INT : is_bool($value) ? PDO::PARAM_STR : PDO::PARAM_STR
);
}
}
After the above changes the error with the boolean values was solved.
But, now I am facing strange issues on the server, when I check the database log error I consistently get the below error.
ERROR: prepared statement "pdo_stmt_00000001" already exists
STATEMENT: set names 'utf8'
ERROR: prepared statement "pdo_stmt_00000001" does not exist
STATEMENT: DEALLOCATE pdo_stmt_00000001
It really was strange, and after exploring the internet I have done the below changes in my database.php file, to disable the prepare statements.
'pgsql' => [
'driver' => 'pgsql',
'host' => env('DB_HOST', '127.0.0.1'),
'port' => env('DB_PORT', '5432'),
'database' => env('DB_DATABASE', 'forge'),
'username' => env('DB_USERNAME', 'forge'),
'password' => env('DB_PASSWORD', ''),
'charset' => 'utf8',
'collation' => 'utf8_unicode_ci',
'prefix' => '',
'schema' => 'public',
'sslmode' => 'prefer',
'options' => [
\PDO::ATTR_EMULATE_PREPARES => true
]
]
The reason behind seeting ATTR_EMULATE_PREPARES => true is becasue I have set "Transaction" mode in "pgbouncer.ini" file.
Now, to make prepared statements work in Transaction mode would need PgBouncer to keep track of them internally, which it does not do. So only way to keep using PgBouncer in this mode is to disable prepared statements in the client, which in my case is PHP Laravel and I have already handle it in the "database.php" file when the connection is made as shown in above code.
I have tried all the options, which are given in http://www.pgbouncer.org/faq.html#how-to-use-prepared-statements-with-transaction-pooling but it doesnot solve the prepare statment error shown in the database log.
ERROR: prepared statement "pdo_stmt_00000001" already exists
STATEMENT: set names 'utf8'
ERROR: prepared statement "pdo_stmt_00000001" does not exist
STATEMENT: DEALLOCATE pdo_stmt_00000001
Please guide me on the same and what further settings are required for the error. Those errors are on the client production server and we cannot go ahead with those errors in production server.
Please give me your valuable feedback at the earliest as I am facing the issue since 5 days and try with all the options that come across.
Thanks!

1) First, you need to change the PDO option you are giving in the options in the pgsql array of your database.php the right way is as given below.
'pgsql' => [
'driver' => 'pgsql',
'host' => env('DB_HOST', '127.0.0.1'),
'port' => env('DB_PORT', '5434'),
'database' => env('DB_DATABASE', 'forge'),
'username' => env('DB_USERNAME', 'forge'),
'password' => env('DB_PASSWORD', ''),
'charset' => 'utf8',
'prefix' => '',
'schema' => 'public',
'sslmode' => 'prefer',
'options' => [
PDO::ATTR_EMULATE_PREPARES => true
]
]
2) Second, and the most important thing is to make sure that you use set the "ATTR_EMULATE_PREPARES" to "true" with each database connection you try to connect in your Database.php file.
For example,
'test' => [
'driver' => 'pgsql',
'host' => env('test', '127.0.0.1'),
'port' => env('test', '5434'),
'database' => env('DB_TEST_DATABASE', 'test'),
'username' => env('DB_USERNAME', 'test'),
'password' => env('DB_PASSWORD', ''),
'charset' => 'utf8',
'prefix' => '',
'schema' => 'public',
'sslmode' => 'prefer',
'options' => [
PDO::ATTR_EMULATE_PREPARES => true
]
],
'test1' => [
'driver' => 'pgsql',
'host' => env('test1', '127.0.0.1'),
'port' => env('test1', '5434'),
'database' => env('DB_TEST1_DATABASE', 'test1'),
'username' => env('DB_USERNAME', 'test'),
'password' => env('DB_PASSWORD', ''),
'charset' => 'utf8',
'prefix' => '',
'schema' => 'public',
'sslmode' => 'prefer',
'options' => [
PDO::ATTR_EMULATE_PREPARES => true
]
]
Please make sure to use the "ATTR_EMULATE_PREPARES" to true for each database connection you make in your application, in your comments you make connection with only "pgsql" which emphasis for postgres sql connection only, and not with the database that your application communicates which is in postgres.
Hope this helps you to resolve your query. Enjoy!!!

First you never need to modify the vendor code, instead you can use attribute casting from your model.
From laravel.com/docs/master/eloquent-mutators#attribute-casting
The $casts property on your model provides a convenient method of converting attributes to common data types. The $casts property should be an array where the key is the name of the attribute being cast and the value is the type you wish to cast the column to. The supported cast types are: integer, real, float, double, decimal:, string, boolean, object, array, collection, date, datetime, and timestamp. When casting to decimal, you must define the number of digits (decimal:2).
To demonstrate attribute casting, let's cast the is_admin attribute,
which is stored in our database as an integer (0 or 1) to a boolean
value:
So in your case you will need to cast revoked to bool by adding to your Eloquent model the $casts property as follows:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class YourModel extends Model
{
/**
* The attributes that should be cast to native types.
*
* #var array
*/
protected $casts = [
'revoked' => 'boolean',
];
}
And for your pgBouncer issue it seems that pgBouncer have an internal issue with transaction pooling and prepared statements,
From: stackoverflow.com/a/7612639/7047493
This turned out to be a pgBouncer issue that occurs when using anything other than session pooling. We were using transaction pooling, which apparently can't support prepared statements. By switching to session pooling, we got around the issue.

None of the previous answers fully worked in our case. In our setup (Laravel + PostgreSQL + pgBouncer), we had enabled these 2 settings in the database.php file. The goal was to make our php backend compatible with pgBouncer and that's what we had done:
// database.php
'options' => array(
PDO::ATTR_EMULATE_PREPARES => true
),
'binary_parameters' => 'yes', // not sure if this one is necessary
These 2 settings partially worked, meaning we were able to run our backend without getting the prepared statement does not exist anymore. Unfortunately for us, we were then getting the datatype mismatch: 7 ERROR: column “xxx” is of type boolean but expression is of type integer just like Nileshsinh Rathod.
Hopefully for us, we came across this post on Github which fixed everything for us. The goal is to override the default PostgresConnector.
And here is a recap of what we did:
Add these 3 files in our project:
https://github.com/umbrellio/laravel-pg-extensions/blob/master/src/Connectors/ConnectionFactory.php
https://github.com/umbrellio/laravel-pg-extensions/blob/master/src/UmbrellioPostgresProvider.php
https://github.com/umbrellio/laravel-pg-extensions/blob/master/src/PostgresConnection.php
Within this file, we only kept the bindValues and prepareBindings functions.
Then, in our config/app.php, we registered the PostgresProvider like so
'providers' => [
App\Providers\ScPostgresProvider::class,
],
Finally, we commented out this line in our AppServiceProvider file in order to make sure only the new one would be registered
public function register()
{
// not used anymore since we use our our own connector
// $this->app->bind('db.connector.pgsql', OldPostgresConnector::class);
}
Thanks a lot to the post of Umbrellio team on Github and hope this answer will help others!

Related

unsupported driver [https], laravel when deployed to heroku

I am trying to deploy a Laravel application to Heroku and connect it with a database which has already been deployed to Azure.
But I am having error "unsupported driver[https]".
My database.php:
<?php
use Illuminate\Support\Str;
return [
'default' => env('DB_CONNECTION', 'mysql'),
/
'mysql' => [
'driver' => 'mysql'
'url' => env('DATABASE_URL','https://firstsqlaap.scm.azurewebsites.net/phpMyAdmin/db_structure.php?server=1&db=localdb&token=51b0b3471e798a712e129bcd1ebe5b01'),
'host' => env('DB_HOST', '127.0.0.1'),
'port' => env('DB_PORT', '53082'),
'database' => env('DB_DATABASE', 'localdb'),
'username' => env('DB_USERNAME', 'user'),
'password' => env('DB_PASSWORD', 'pass'),
'charset' => 'utf8mb4',
'collation' => 'utf8mb4_unicode_ci',
'options' => extension_loaded('pdo_mysql') ? array_filter([
PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'),
]) : [],
],
];
My SESSION_DRIVER is set to database because when set to file it was saying 419 error. I do not have any migration files as my database is deployed to Azure.
How to resolve this issue?
This certainly isn't the right URL to use:
https://firstsqlaap.scm.azurewebsites.net/phpMyAdmin/db_structure.php
You appear to be pointing to an instance of phpMyAdmin. phpMyAdmin isn't a database server, it's a dataase client. It's a tool that you might use to interact with your database. You need to provide the URL to your actual database.
Your database URL should look more like this:
driver://username:password#host:port/database?options
For MySQL, driver:// is likely mysql://.
I don't have any MySQL databases running on Azure, but it looks like a real URL might be something like
mysql://user:password#your-database-instance.mysql.database.azure.com/your-database-name
Go into the Azure portal and navigate to your database instance. Then, in the left navigation panel, click on "Connection strings". The information you need should be there, though not in URL format. You can either build your own URL by plugging the right values in or use the individual settings in your config/database.php file.
I commented url and it work for me

invalid value for parameter "client_encoding": "utf8mb4" (SQL: select * from "tablename") when deploy laravel to heroku [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 1 year ago.
Improve this question
I am trying to deploy my laravel app to heroku but it returns an error:
SQLSTATE[22023]: Invalid parameter value: 7 ERROR: invalid value for parameter "client_encoding": "utf8mb4" (SQL: select * from "tablename").
I changed my tablename collation and columns from utf8mb4_unicode_ci to utf8unicode_ci but nothing happened. Please help me. I tried all the possible solutions I've searched but nothing really works.
I have faced the same problem. Mistakenly i have not added DB_CONNECTION=pgsql in config vars on Heroku dashboard. Inside setting tab click on Reveal Config Vars and add DB_CONNECTION=pgsql there.
Another way just simply add config vars using terminal.
heroku config:add DB_CONNECTION=pgsql
After setting up your psql details as specified up there on config/database.php
run
git push origin master
git push heroku master
And try run the bash again..
heroku run bash
php artisan migrate:fresh
yes
and you're good to go
Laravel Databse Connection Environment issue
You are probably using the Postgresql as the Database instead of MySql.
Then please configure the database config in config/database.php file
'default' => env('DB_CONNECTION', 'pgsql')
or set the DB_CONNECTION in .env as pgsql
pgsql will not support utf8mb4, it is for the MySQL, if you are using Postgresql pls correct the connection environment.
For mysql:
[
'database.connections.rds' => [
'driver' => 'mysql',
'host' => $endpoint,
'port' => $port,
'database' => $db_name,
'username' => $user,
'password' => $password,
'unix_socket' => env('DB_SOCKET', ''),
'charset' => 'utf8mb4',
'collation' => 'utf8mb4_unicode_ci',
'prefix' => '',
'strict' => true,
'engine' => null,
]
]
for Postgres
[
'database.connections.rds' => [
'driver' => 'pgsql',
'host' => $endpoint,
'port' => $port,
'database' => $db_name,
'username' => $user,
'password' => $password,
'unix_socket' => env('DB_SOCKET', ''),
'charset' => 'utf8',
'collation' => 'utf8_general_ci',
'prefix' => '',
'strict' => true,
'engine' => null,
]
]
Are you using laravel/telescope?
I had the same problem when trying to implement my project. To resolve the error, I removed laravel/telescope from my project.
'mysql' => [
// ...
'charset' => 'utf8',
'collation' => 'utf8_general_ci',
// ...
],
This happens at the environment variable configuration level on Heroku if you haven't done key = DB_CONNECTION and value = pgsql.
In config/database.php, Use default connection
'default' => 'pgsql',
"client_encoding": "utf8"
If you are using MySQL than change default connection to
'default' => 'mysql',
"client_encoding": "utf8mb4"

How to use Athena with Laravel?

We're looking to have a specific search/aggregate/graphs page use AWS Athena instead of Postgres.
We've tried 2 ODBC packages for laravel, and discovered that Laravel uses prepared statements and Athena's Simba ODBC driver does not support prepared statements. PDO supports emulating prepared statements, which sounds like exactly what would work, but enabling ATTR_EMULATE_PREPARES doesn't make any difference.
This is the error we're getting:
Illuminate/Database/QueryException with message 'SQLSTATE[42000]: Syntax error or access violation: 0 [Simba][Athena] (1040) An error has been thrown from the AWS Athena client. Athena Error No: 102, HTTP Response Code: 1, Error Message: SYNTAX_ERROR: line 1:1: Incorrect number of parameters: expected 1 but found 0 [Execution ID: ] (SQLPrepare[0] at /build/php7.2-PL0pac/php7.2-7.2.16/ext/pdo_odbc/odbc_driver.c:206) (SQL: select * from "qa_lines_csv" where "id" > 100 limit 1)'
Our database config:
'test_athena' => [
'driver' => 'odbc',
'dsn' => 'odbc:Driver=/opt/simba/athenaodbc/lib/64/libathenaodbc_sb64.so;'
.'AwsRegion=us-east-1;'
.'AuthenticationType=IAM Credentials;'
.'UID=<redacted>;'
.'PWD=<redacted>;'
.'S3OutputLocation=s3://<redacted>/;',
'host' => env('ATHENA_HOST', 'localhost'),
'port' => env('ATHENA_PORT', '5432'),
'database' => env('ATHENA_DATABASE', 'forge'),
'username' => env('ATHENA_USERNAME', 'forge'),
'password' => env('ATHENA_PASSWORD', ''),
'charset' => 'utf8',
'prefix' => '',
'schema' => 'public',
'options' => [
\PDO::ATTR_EMULATE_PREPARES => true,
],
$pdo = DB::connection('test_athena')->getPdo();
// this crashes
$pdo->prepare('select * from "qa_lines_csv" where "id" > ? limit 1')->execute([100]);
// this does not crash
$pdo->prepare('select * from "qa_lines_csv" where "id" > 100 limit 1')->execute([]);
// this crashes
DB::connection('test_athena')->table('qa_lines_csv')->where('id', '>', 100)->first()
It seems like there is a bug between PDO and the Simba driver, one of them is not allowing the PDO emulation to work. Athena should just start supporting prepared statements.
We've already written our application, so we can't re-write everything to not use Laravel's query builder and manually concatenate strings into queries.

how to exclude some block statement from transaction - Laravel 5.7

I need some statement (that's exist in transaction block) to be executed directly to Database without transaction , example :
DB::beginTransaction();
//query A : insert or update to some tables in transaction
//**I need some of the result of (query A) to be saved in Database without transaction**
//insert or update to some tables in transaction
DB::commit();
[EDIT] After reading your question again, it seems i may have misundersood some parts. If by saying
I need some of the result of (query A) to be saved in Database without transaction
You mean "use the values inserted within the transaction before the transaction end", i guess it's not possible. You can still use my answer to insert or update data instantaneously, but you'll not be able to read something you inserted in the transaction before the transaction end.
You can duplicate the connection you are using for the transaction in database.php. For this example, let's assume you call it 'mysql_outside_transaction'. You should have something like this in the database.php file, after your default connection:
'mysql_outside_transaction' => [
'driver' => 'mysql',
'host' => env('DB_HOST', '127.0.0.1'),
'port' => env('DB_PORT', '3306'),
'database' => env('DB_DATABASE', 'forge'),
'username' => env('DB_USERNAME', 'forge'),
'password' => env('DB_PASSWORD', ''),
'unix_socket' => env('DB_SOCKET', ''),
'charset' => 'utf8mb4',
'collation' => 'utf8mb4_unicode_ci',
'prefix' => '',
'strict' => true,
'engine' => null,
],
From there, since you have a second connection to the same database available, you can use it to save the data you need directly within the transaction.
Again, to make an example, if you have a instance of $modelA, you can tell it to use your second connection: $modelA->setConnection('mysql_outside_transaction');. From there, every database operation originating from this instance will be done outside your transaction (because it'll use the second connection you configured and not the one used for the transaction).
Don't forget you can set the connection to use in many ways. It could be directly on model class attributes (protected $connection = 'your_connection';), with the DB facade (DB::connection('your_connection');) and probably in other ways i don't think about right now :)

Laravel 5l Multiple Database Connections

Do I need to have my DB connection info in 2 places? If I do the below, I connect just fine. If I remove either file's data, I can't connect.
config/databases.php file
'blah_1' => [
'driver' => 'mysql',
'host' => env('DB_HOST’,’1.1.1.1’),
'port' => env('DB_PORT','3306'),
'database' => env('DB_DATABASE’,’someDB_1’),
'username' => env('DB_USERNAME’,’someUser_1’),
'password' => env('DB_PASSWORD’,’somePass_1’),
],
'blah_2' => [
'driver' => 'mysql',
'host' => env('DB_HOST_SECOND’,’2.2.2.2’),
'port' => env('DB_PORT_SECOND','3306'),
'database' => env('DB_DATABASE_SECOND’,’someDB_2’),
'username' => env('DB_USERNAME_SECOND’,’someUser_2’),
'password' => env('DB_PASSWORD_SECOND’,’somePass_2’),
],
.env file:
DB_CONNECTION=blah_1
DB_HOST=1.1.1.1
DB_PORT=3306
DB_DATABASE=someDB_1
DB_USERNAME=someUser_1
DB_PASSWORD=somePass_1
DB_CONNECTION_SECOND=blah_2
DB_HOST_SECOND=2.2.2.2
DB_PORT_SECOND=3306
DB_DATABASE_SECOND=someDB_2
DB_USERNAME_SECOND=someUser_2
DB_PASSWORD_SECOND=somePass_2
The short answer is no. You only need it in your config/databases.php. The .env file is to overwrite the settings in your other environments without updating the configuration file.
For example, in your local environment, your credentials is most likely different from your production environment. You wouldn't want to update config/databases.php locally and remind yourself to not push the file.
However, the connections would still work even if remove them from the .env file. It will use the second parameter's value in your env() as default.

Resources