From 02c8019bb8a25c9b9f827030d5224c2f7338e44a Mon Sep 17 00:00:00 2001 From: Sean Harvey Date: Tue, 20 Mar 2012 21:20:35 +1300 Subject: [PATCH] ENHANCEMENT Introduce Database::prepStringForDB(), used by DBField::prepValueForDB() and StringField::prepValueForDB() to ensure the field value is escaped correctly for the database. This means databases like MSSQL can introduce an "N" prefix (marking text as unicode to be saved correctly) by overloading the prepStringForDB method. MySQL, PostgreSQL and SQLite3 operate as usual. --- model/Database.php | 14 +++++- model/fieldtypes/DBField.php | 4 +- model/fieldtypes/StringField.php | 4 +- tests/model/DBFieldTest.php | 78 ++++++++++++++++---------------- 4 files changed, 57 insertions(+), 43 deletions(-) diff --git a/model/Database.php b/model/Database.php index 0af0b8353..dfc7d63f5 100644 --- a/model/Database.php +++ b/model/Database.php @@ -757,7 +757,19 @@ abstract class SS_Database { } return $text; } - + + /** + * Wrap a string into DB-specific quotes. MySQL, PostgreSQL and SQLite3 only need single quotes around the string. + * MSSQL will overload this and include it's own N prefix to mark the string as unicode, so characters like macrons + * are saved correctly. + * + * @param string $string String to be prepared for database query + * @return string Prepared string + */ + public function prepStringForDB($string) { + return "'" . Convert::raw2sql($string) . "'"; + } + /** * Function to return an SQL datetime expression that can be used with the adapter in use * used for querying a datetime in a certain format diff --git a/model/fieldtypes/DBField.php b/model/fieldtypes/DBField.php index 5e7d80d2c..8769099b2 100644 --- a/model/fieldtypes/DBField.php +++ b/model/fieldtypes/DBField.php @@ -143,9 +143,9 @@ abstract class DBField extends ViewableData { if($value === null || $value === "" || $value === false) { return "null"; } else { - return "'" . Convert::raw2sql($value) . "'"; + return DB::getConn()->prepStringForDB($value); } - } + } /** * Prepare the current field for usage in a diff --git a/model/fieldtypes/StringField.php b/model/fieldtypes/StringField.php index 0f843ef09..5010679ed 100644 --- a/model/fieldtypes/StringField.php +++ b/model/fieldtypes/StringField.php @@ -75,8 +75,8 @@ abstract class StringField extends DBField { * @see core/model/fieldtypes/DBField#prepValueForDB($value) */ function prepValueForDB($value) { - if ( !$this->nullifyEmpty && $value === '' ) { - return "'" . Convert::raw2sql($value) . "'"; + if(!$this->nullifyEmpty && $value === '') { + return DB::getConn()->prepStringForDB($value); } else { return parent::prepValueForDB($value); } diff --git a/tests/model/DBFieldTest.php b/tests/model/DBFieldTest.php index 16e04548a..b62988d6d 100644 --- a/tests/model/DBFieldTest.php +++ b/tests/model/DBFieldTest.php @@ -22,7 +22,8 @@ class DBFieldTest extends SapphireTest { * Test the prepValueForDB() method on DBField. */ function testPrepValueForDB() { - + $db = DB::getConn(); + /* Float behaviour, asserting we have 0 */ $this->assertEquals('0', singleton('Float')->prepValueForDB(0)); $this->assertEquals('0', singleton('Float')->prepValueForDB(null)); @@ -74,63 +75,64 @@ class DBFieldTest extends SapphireTest { $this->assertEquals("'1'", singleton('Boolean')->prepValueForDB('1')); /* Varchar behaviour */ + $this->assertEquals($db->prepStringForDB("0"), singleton('Varchar')->prepValueForDB(0)); $this->assertEquals("'0'", singleton('Varchar')->prepValueForDB(0)); $this->assertEquals("null", singleton('Varchar')->prepValueForDB(null)); $this->assertEquals("null", singleton('Varchar')->prepValueForDB(false)); $this->assertEquals("null", singleton('Varchar')->prepValueForDB('')); - $this->assertEquals("'0'", singleton('Varchar')->prepValueForDB('0')); - $this->assertEquals("'1'", singleton('Varchar')->prepValueForDB(1)); - $this->assertEquals("'1'", singleton('Varchar')->prepValueForDB(true)); - $this->assertEquals("'1'", singleton('Varchar')->prepValueForDB('1')); - $this->assertEquals("'00000'", singleton('Varchar')->prepValueForDB('00000')); - $this->assertEquals("'0'", singleton('Varchar')->prepValueForDB(0000)); - $this->assertEquals("'test'", singleton('Varchar')->prepValueForDB('test')); - $this->assertEquals("'123'", singleton('Varchar')->prepValueForDB(123)); + $this->assertEquals($db->prepStringForDB("0"), singleton('Varchar')->prepValueForDB('0')); + $this->assertEquals($db->prepStringForDB("1"), singleton('Varchar')->prepValueForDB(1)); + $this->assertEquals($db->prepStringForDB("1"), singleton('Varchar')->prepValueForDB(true)); + $this->assertEquals($db->prepStringForDB("1"), singleton('Varchar')->prepValueForDB('1')); + $this->assertEquals($db->prepStringForDB("00000"), singleton('Varchar')->prepValueForDB('00000')); + $this->assertEquals($db->prepStringForDB("0"), singleton('Varchar')->prepValueForDB(0000)); + $this->assertEquals($db->prepStringForDB("test"), singleton('Varchar')->prepValueForDB('test')); + $this->assertEquals($db->prepStringForDB("123"), singleton('Varchar')->prepValueForDB(123)); /* AllowEmpty Varchar behaviour */ $varcharField = new Varchar("testfield", 50, array("nullifyEmpty"=>false)); - $this->assertSame("'0'", $varcharField->prepValueForDB(0)); + $this->assertSame($db->prepStringForDB("0"), $varcharField->prepValueForDB(0)); $this->assertSame("null", $varcharField->prepValueForDB(null)); $this->assertSame("null", $varcharField->prepValueForDB(false)); - $this->assertSame("''", $varcharField->prepValueForDB('')); - $this->assertSame("'0'", $varcharField->prepValueForDB('0')); - $this->assertSame("'1'", $varcharField->prepValueForDB(1)); - $this->assertSame("'1'", $varcharField->prepValueForDB(true)); - $this->assertSame("'1'", $varcharField->prepValueForDB('1')); - $this->assertSame("'00000'", $varcharField->prepValueForDB('00000')); - $this->assertSame("'0'", $varcharField->prepValueForDB(0000)); - $this->assertSame("'test'", $varcharField->prepValueForDB('test')); - $this->assertSame("'123'", $varcharField->prepValueForDB(123)); + $this->assertSame($db->prepStringForDB(""), $varcharField->prepValueForDB('')); + $this->assertSame($db->prepStringForDB("0"), $varcharField->prepValueForDB('0')); + $this->assertSame($db->prepStringForDB("1"), $varcharField->prepValueForDB(1)); + $this->assertSame($db->prepStringForDB("1"), $varcharField->prepValueForDB(true)); + $this->assertSame($db->prepStringForDB("1"), $varcharField->prepValueForDB('1')); + $this->assertSame($db->prepStringForDB("00000"), $varcharField->prepValueForDB('00000')); + $this->assertSame($db->prepStringForDB("0"), $varcharField->prepValueForDB(0000)); + $this->assertSame($db->prepStringForDB("test"), $varcharField->prepValueForDB('test')); + $this->assertSame($db->prepStringForDB("123"), $varcharField->prepValueForDB(123)); unset($varcharField); /* Text behaviour */ - $this->assertEquals("'0'", singleton('Text')->prepValueForDB(0)); + $this->assertEquals($db->prepStringForDB("0"), singleton('Text')->prepValueForDB(0)); $this->assertEquals("null", singleton('Text')->prepValueForDB(null)); $this->assertEquals("null", singleton('Text')->prepValueForDB(false)); $this->assertEquals("null", singleton('Text')->prepValueForDB('')); - $this->assertEquals("'0'", singleton('Text')->prepValueForDB('0')); - $this->assertEquals("'1'", singleton('Text')->prepValueForDB(1)); - $this->assertEquals("'1'", singleton('Text')->prepValueForDB(true)); - $this->assertEquals("'1'", singleton('Text')->prepValueForDB('1')); - $this->assertEquals("'00000'", singleton('Text')->prepValueForDB('00000')); - $this->assertEquals("'0'", singleton('Text')->prepValueForDB(0000)); - $this->assertEquals("'test'", singleton('Text')->prepValueForDB('test')); - $this->assertEquals("'123'", singleton('Text')->prepValueForDB(123)); + $this->assertEquals($db->prepStringForDB("0"), singleton('Text')->prepValueForDB('0')); + $this->assertEquals($db->prepStringForDB("1"), singleton('Text')->prepValueForDB(1)); + $this->assertEquals($db->prepStringForDB("1"), singleton('Text')->prepValueForDB(true)); + $this->assertEquals($db->prepStringForDB("1"), singleton('Text')->prepValueForDB('1')); + $this->assertEquals($db->prepStringForDB("00000"), singleton('Text')->prepValueForDB('00000')); + $this->assertEquals($db->prepStringForDB("0"), singleton('Text')->prepValueForDB(0000)); + $this->assertEquals($db->prepStringForDB("test"), singleton('Text')->prepValueForDB('test')); + $this->assertEquals($db->prepStringForDB("123"), singleton('Text')->prepValueForDB(123)); /* AllowEmpty Text behaviour */ $textField = new Text("testfield", array("nullifyEmpty"=>false)); - $this->assertSame("'0'", $textField->prepValueForDB(0)); + $this->assertSame($db->prepStringForDB("0"), $textField->prepValueForDB(0)); $this->assertSame("null", $textField->prepValueForDB(null)); $this->assertSame("null", $textField->prepValueForDB(false)); - $this->assertSame("''", $textField->prepValueForDB('')); - $this->assertSame("'0'", $textField->prepValueForDB('0')); - $this->assertSame("'1'", $textField->prepValueForDB(1)); - $this->assertSame("'1'", $textField->prepValueForDB(true)); - $this->assertSame("'1'", $textField->prepValueForDB('1')); - $this->assertSame("'00000'", $textField->prepValueForDB('00000')); - $this->assertSame("'0'", $textField->prepValueForDB(0000)); - $this->assertSame("'test'", $textField->prepValueForDB('test')); - $this->assertSame("'123'", $textField->prepValueForDB(123)); + $this->assertSame($db->prepStringForDB(""), $textField->prepValueForDB('')); + $this->assertSame($db->prepStringForDB("0"), $textField->prepValueForDB('0')); + $this->assertSame($db->prepStringForDB("1"), $textField->prepValueForDB(1)); + $this->assertSame($db->prepStringForDB("1"), $textField->prepValueForDB(true)); + $this->assertSame($db->prepStringForDB("1"), $textField->prepValueForDB('1')); + $this->assertSame($db->prepStringForDB("00000"), $textField->prepValueForDB('00000')); + $this->assertSame($db->prepStringForDB("0"), $textField->prepValueForDB(0000)); + $this->assertSame($db->prepStringForDB("test"), $textField->prepValueForDB('test')); + $this->assertSame($db->prepStringForDB("123"), $textField->prepValueForDB(123)); unset($textField); /* Time behaviour */