Skip to content

Commit

Permalink
fixed boolean handling for PostgreSQL
Browse files Browse the repository at this point in the history
- do not allow boolean values for integer columns
- use native boolean type with boolean values
- removed workaround that turned out to be wrong when schema and values
  are used correctly. Workaround resulted from wrong usage of boolean
  values before.

fixes yiisoft#4672
  • Loading branch information
cebe committed Aug 28, 2014
1 parent f562cbc commit 78b5c7d
Show file tree
Hide file tree
Showing 5 changed files with 147 additions and 37 deletions.
1 change: 1 addition & 0 deletions framework/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ Yii Framework 2 Change Log
- Bug #4519: `yii\base\Model::isAttributeRequired()` should check if the `when` option of the validator is set (thiagotalma)
- Bug #4592: Fixed `yii help` command was listing incorrect action names for methods like `actionSayNO` (samdark)
- Bug #4654: Fixed issue with PostgreSQL and inserting boolean values with batch insert (cebe)
- Bug #4672: Fixed issue with PostgreSQL handling of boolean values in queries, dropped support for using boolean value for integer columns (cebe)
- Bug #4727: Fixed wrong Stylus definition in `\yii\web\AssetConverter` (samdark)
- Bug #4813: Fixed MSSQL schema that was getting incorrect info about constraints (samdark, SerjRamone, o-rey)
- Bug: Fixed inconsistent return of `\yii\console\Application::runAction()` (samdark)
Expand Down
23 changes: 0 additions & 23 deletions framework/db/pgsql/Schema.php
Original file line number Diff line number Diff line change
Expand Up @@ -166,29 +166,6 @@ public function loadTableSchema($name)
}
}

/**
* Determines the PDO type for the given PHP data value.
* @param mixed $data the data whose PDO type is to be determined
* @return integer the PDO type
* @see http://www.php.net/manual/en/pdo.constants.php
*/
public function getPdoType($data)
{
// php type => PDO type
static $typeMap = [
// https://github.com/yiisoft/yii2/issues/1115
// Cast boolean to integer values to work around problems with PDO casting false to string '' https://bugs.php.net/bug.php?id=33876
'boolean' => \PDO::PARAM_INT,
'integer' => \PDO::PARAM_INT,
'string' => \PDO::PARAM_STR,
'resource' => \PDO::PARAM_LOB,
'NULL' => \PDO::PARAM_NULL,
];
$type = gettype($data);

return isset($typeMap[$type]) ? $typeMap[$type] : \PDO::PARAM_STR;
}

/**
* Returns all table names in the database.
* @param string $schema the schema of the tables. Defaults to empty string, meaning the current or default schema.
Expand Down
11 changes: 6 additions & 5 deletions tests/unit/data/postgres.sql
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ CREATE TABLE "customer" (
name varchar(128),
address text,
status integer DEFAULT 0,
bool_status boolean DEFAULT FALSE,
profile_id integer
);

Expand Down Expand Up @@ -109,8 +110,8 @@ CREATE TABLE "type" (
blob_col bytea,
numeric_col decimal(5,2) DEFAULT '33.22',
time timestamp NOT NULL DEFAULT '2002-01-01 00:00:00',
bool_col smallint NOT NULL,
bool_col2 smallint DEFAULT '1',
bool_col boolean NOT NULL,
bool_col2 boolean DEFAULT TRUE,
ts_default TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
bit_col BIT(8) NOT NULL DEFAULT B'10000010'
);
Expand All @@ -125,9 +126,9 @@ CREATE TABLE "bool_values" (
INSERT INTO "profile" (description) VALUES ('profile customer 1');
INSERT INTO "profile" (description) VALUES ('profile customer 3');

INSERT INTO "customer" (email, name, address, status, profile_id) VALUES ('user1@example.com', 'user1', 'address1', 1, 1);
INSERT INTO "customer" (email, name, address, status) VALUES ('user2@example.com', 'user2', 'address2', 1);
INSERT INTO "customer" (email, name, address, status, profile_id) VALUES ('user3@example.com', 'user3', 'address3', 2, 2);
INSERT INTO "customer" (email, name, address, status, bool_status, profile_id) VALUES ('user1@example.com', 'user1', 'address1', 1, true, 1);
INSERT INTO "customer" (email, name, address, status, bool_status) VALUES ('user2@example.com', 'user2', 'address2', 1, true);
INSERT INTO "customer" (email, name, address, status, bool_status, profile_id) VALUES ('user3@example.com', 'user3', 'address3', 2, false, 2);

INSERT INTO "category" (name) VALUES ('Books');
INSERT INTO "category" (name) VALUES ('Movies');
Expand Down
128 changes: 127 additions & 1 deletion tests/unit/framework/db/pgsql/PostgreSQLActiveRecordTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,12 @@

namespace yiiunit\framework\db\pgsql;

use yii\behaviors\TimestampBehavior;
use yii\db\pgsql\Schema;
use yiiunit\data\ar\ActiveRecord;
use yiiunit\framework\ar\ActiveRecordTestTrait;
use yiiunit\framework\db\ActiveRecordTest;
use yiiunit\TestCase;

/**
* @group db
Expand All @@ -13,6 +17,72 @@ class PostgreSQLActiveRecordTest extends ActiveRecordTest
{
protected $driverName = 'pgsql';

public function testBooleanAttribute()
{
/* @var $customerClass \yii\db\ActiveRecordInterface */
$customerClass = $this->getCustomerClass();
/* @var $this TestCase|ActiveRecordTestTrait */
$customer = new $customerClass();
$customer->name = 'boolean customer';
$customer->email = 'mail@example.com';
$customer->bool_status = false;
$customer->save(false);

$customer->refresh();
$this->assertSame(false, $customer->bool_status);

$customer->bool_status = true;
$customer->save(false);

$customer->refresh();
$this->assertSame(true, $customer->bool_status);

$customers = $customerClass::find()->where(['bool_status' => true])->all();
$this->assertEquals(3, count($customers));

$customers = $customerClass::find()->where(['bool_status' => false])->all();
$this->assertEquals(1, count($customers));
}

public function testFindAsArray()
{
/* @var $customerClass \yii\db\ActiveRecordInterface */
$customerClass = $this->getCustomerClass();

// asArray
$customer = $customerClass::find()->where(['id' => 2])->asArray()->one();
$this->assertEquals([
'id' => 2,
'email' => 'user2@example.com',
'name' => 'user2',
'address' => 'address2',
'status' => 1,
'profile_id' => null,
'bool_status' => true,
], $customer);

// find all asArray
$customers = $customerClass::find()->asArray()->all();
$this->assertEquals(3, count($customers));
$this->assertArrayHasKey('id', $customers[0]);
$this->assertArrayHasKey('name', $customers[0]);
$this->assertArrayHasKey('email', $customers[0]);
$this->assertArrayHasKey('address', $customers[0]);
$this->assertArrayHasKey('status', $customers[0]);
$this->assertArrayHasKey('bool_status', $customers[0]);
$this->assertArrayHasKey('id', $customers[1]);
$this->assertArrayHasKey('name', $customers[1]);
$this->assertArrayHasKey('email', $customers[1]);
$this->assertArrayHasKey('address', $customers[1]);
$this->assertArrayHasKey('status', $customers[1]);
$this->assertArrayHasKey('bool_status', $customers[1]);
$this->assertArrayHasKey('id', $customers[2]);
$this->assertArrayHasKey('name', $customers[2]);
$this->assertArrayHasKey('email', $customers[2]);
$this->assertArrayHasKey('address', $customers[2]);
$this->assertArrayHasKey('status', $customers[2]);
$this->assertArrayHasKey('bool_status', $customers[2]);
}

public function testBooleanValues()
{
Expand Down Expand Up @@ -40,6 +110,42 @@ public function testBooleanValues()
$this->assertSame(false, BoolAR::find()->where(['bool_col' => false])->one($db)->bool_col);
}

/**
* https://github.com/yiisoft/yii2/issues/4672
*/
public function testBooleanValues2()
{
$db = $this->getConnection();
$db->charset = 'utf8';

$db->createCommand("DROP TABLE IF EXISTS bool_user;")->execute();
$db->createCommand()->createTable('bool_user', [
'id' => Schema::TYPE_PK,
'username' => Schema::TYPE_STRING . ' NOT NULL',
'auth_key' => Schema::TYPE_STRING . '(32) NOT NULL',
'password_hash' => Schema::TYPE_STRING . ' NOT NULL',
'password_reset_token' => Schema::TYPE_STRING,
'email' => Schema::TYPE_STRING . ' NOT NULL',
'role' => Schema::TYPE_SMALLINT . ' NOT NULL DEFAULT 10',

'status' => Schema::TYPE_SMALLINT . ' NOT NULL DEFAULT 10',
'created_at' => Schema::TYPE_INTEGER . ' NOT NULL',
'updated_at' => Schema::TYPE_INTEGER . ' NOT NULL',
])->execute();
$db->createCommand()->addColumn('bool_user', 'is_deleted', Schema::TYPE_BOOLEAN . ' NOT NULL DEFAULT FALSE')->execute();

$user = new UserAR();
$user->username = 'test';
$user->auth_key = 'test';
$user->password_hash = 'test';
$user->email = 'test@example.com';
$user->save(false);

$this->assertEquals(1, count(UserAR::find()->where(['is_deleted' => false])->all($db)));
$this->assertEquals(0, count(UserAR::find()->where(['is_deleted' => true])->all($db)));
$this->assertEquals(1, count(UserAR::find()->where(['is_deleted' => [true, false]])->all($db)));
}

public function testBooleanDefaultValues()
{
$model = new BoolAR();
Expand All @@ -61,4 +167,24 @@ public static function tableName()
{
return 'bool_values';
}
}
}

class UserAR extends ActiveRecord
{
const STATUS_DELETED = 0;
const STATUS_ACTIVE = 10;
const ROLE_USER = 10;

public static function tableName()
{
return '{{%bool_user}}';
}

public function behaviors()
{
return [
TimestampBehavior::className(),
];
}
}

21 changes: 13 additions & 8 deletions tests/unit/framework/db/pgsql/PostgreSQLSchemaTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -47,14 +47,19 @@ public function getExpectedColumns()
$columns['blob_col']['type'] = 'binary';
$columns['numeric_col']['dbType'] = 'numeric';
$columns['numeric_col']['size'] = null;
$columns['bool_col']['dbType'] = 'int2';
$columns['bool_col']['type'] = 'boolean';
$columns['bool_col']['phpType'] = 'boolean';
$columns['bool_col']['dbType'] = 'bool';
$columns['bool_col']['size'] = null;
$columns['bool_col']['precision'] = 16;
$columns['bool_col']['scale'] = 0;
$columns['bool_col2']['dbType'] = 'int2';
$columns['bool_col']['precision'] = null;
$columns['bool_col']['scale'] = null;
$columns['bool_col2']['type'] = 'boolean';
$columns['bool_col2']['phpType'] = 'boolean';
$columns['bool_col2']['dbType'] = 'bool';
$columns['bool_col2']['size'] = null;
$columns['bool_col2']['precision'] = 16;
$columns['bool_col2']['scale'] = 0;
$columns['bool_col2']['precision'] = null;
$columns['bool_col2']['scale'] = null;
$columns['bool_col2']['defaultValue'] = true;
$columns['ts_default']['defaultValue'] = new Expression('now()');
$columns['bit_col']['dbType'] = 'bit';
$columns['bit_col']['size'] = 8;
Expand All @@ -71,8 +76,8 @@ public function testGetPDOType()
[0, \PDO::PARAM_INT],
[1, \PDO::PARAM_INT],
[1337, \PDO::PARAM_INT],
[true, \PDO::PARAM_INT],
[false, \PDO::PARAM_INT],
[true, \PDO::PARAM_BOOL],
[false, \PDO::PARAM_BOOL],
[$fp = fopen(__FILE__, 'rb'), \PDO::PARAM_LOB],
];

Expand Down

0 comments on commit 78b5c7d

Please sign in to comment.