diff --git a/core/ClassInfo.php b/core/ClassInfo.php index b8be71c69..9e497cc99 100755 --- a/core/ClassInfo.php +++ b/core/ClassInfo.php @@ -29,8 +29,7 @@ class ClassInfo { */ static function hasTable($class) { if(DB::isActive()) { - $SQL_table = Convert::raw2sql($class); - return (bool)(DB::query("SHOW TABLES LIKE '$SQL_table'")->value()); + return DB::getConn()->hasTable($class); } else { return false; } @@ -40,14 +39,7 @@ class ClassInfo { * Returns the manifest of all classes which are present in the database. */ static function getValidSubClasses(){ - // Get the enum of all page types from the SiteTree table - $classnameinfo = DB::query("DESCRIBE SiteTree ClassName")->first(); - preg_match_all("/'[^,]+'/", $classnameinfo["Type"], $matches); - - foreach($matches[0] as $value) { - $classes[] = trim($value, "'"); - } - return $classes; + return DB::getConn()->enumValuesForField("SiteTree", "ClassName"); } /** diff --git a/core/model/Database.php b/core/model/Database.php index 637348d39..220091be2 100755 --- a/core/model/Database.php +++ b/core/model/Database.php @@ -111,6 +111,22 @@ abstract class Database extends Object { */ protected abstract function tableList(); + + /** + * Returns true if the given table exists in the database + */ + abstract function hasTable($tableName); + + /** + * Returns the enum values available on the given field + */ + abstract function enumValuesForField($tableName, $fieldName); + + /** + * Convert a SQLQuery object into a SQL statement + */ + abstract function sqlQueryToString(SQLQuery $sqlQuery); + /** * The table list, generated by the tableList() function. * Used by the requireTable() function. @@ -297,6 +313,8 @@ abstract class Database extends Object { Profiler::mark('requireField'); + // TODO: This is tempororary + $spec['parts']['name'] = $field; //Convert the $spec array into a database-specific string $spec=DB::getConn()->$spec['type']($spec['parts']); @@ -386,37 +404,37 @@ abstract class Database extends Object { function manipulate($manipulation) { foreach($manipulation as $table => $writeInfo) { if(isset($writeInfo['fields']) && $writeInfo['fields']) { - $fieldList = array(); + $fieldList = $columnList = $valueList = array(); foreach($writeInfo['fields'] as $fieldName => $fieldVal) { $fieldList[] = "\"$fieldName\" = $fieldVal"; + $columnList[] = "\"$fieldName\""; + + // Empty strings inserted as null in INSERTs. Replacement of Database::replace_with_null(). + if($fieldVal === "''") $valueList[] = "null"; + else $valueList[] = $fieldVal; } - $fieldList = implode(", ", $fieldList); if(!isset($writeInfo['where']) && isset($writeInfo['id'])) { - $writeInfo['where'] = "ID = $writeInfo[id]"; + $writeInfo['where'] = "\"ID\" = " . (int)$writeInfo['id']; } switch($writeInfo['command']) { case "update": + $fieldList = implode(", ", $fieldList); $sql = "update \"$table\" SET $fieldList where $writeInfo[where]"; $this->query($sql); - - // If numAffectedRecord = 0, then we want to run instert instead - if(!$this->affectedRows()) { - if(!isset($writeInfo['fields']['ID']) && isset($writeInfo['id'])) { - $fieldList .= ", ID = $writeInfo[id]"; - } - $sql = "insert into \"$table\" SET $fieldList"; - $this->query($sql, null); - } - break; + + // If affectedRows = 0, then don't break, meaning the insert query below gets called + if($this->affectedRows()) break; case "insert": if(!isset($writeInfo['fields']['ID']) && isset($writeInfo['id'])) { - $fieldList .= ", ID = $writeInfo[id]"; + $columnList[] = "\"$ID\""; + $valueList[] = (int)$writeInfo['id']; } - $fieldList = Database::replace_with_null($fieldList); - $sql = "insert into \"$table\" SET $fieldList"; + $columnList = implode(", ", $columnList); + $valueList = implode(", ", $valueList); + $sql = "insert into \"$table\" ($columnList) VALUES ($valueList)"; $this->query($sql); break; diff --git a/core/model/MySQLDatabase.php b/core/model/MySQLDatabase.php index 3e80b2ccb..4ba31d6e3 100644 --- a/core/model/MySQLDatabase.php +++ b/core/model/MySQLDatabase.php @@ -539,6 +539,74 @@ class MySQLDatabase extends Database { return 'varchar(' . $values['precision'] . ') character set utf8 collate utf8_general_ci'; } + + /** + * Returns true if the given table is exists in the current database + * NOTE: Experimental; introduced for db-abstraction and may changed before 2.4 is released. + */ + public function hasTable($table) { + $SQL_table = Convert::raw2sql($table); + return (bool)($this->query("SHOW TABLES LIKE '$SQL_table'")->value()); + } + + /** + * Returns the values of the given enum field + * NOTE: Experimental; introduced for db-abstraction and may changed before 2.4 is released. + */ + public function enumValuesForField($tableName, $fieldName) { + // Get the enum of all page types from the SiteTree table + $classnameinfo = DB::query("DESCRIBE \"$tableName\" \"$fieldName\"")->first(); + preg_match_all("/'[^,]+'/", $classnameinfo["Type"], $matches); + + foreach($matches[0] as $value) { + $classes[] = trim($value, "'"); + } + return $classes; + } + + /** + * Convert a SQLQuery object into a SQL statement + */ + public function sqlQueryToString(SQLQuery $sqlQuery) { + if (!$sqlQuery->from) return ''; + $distinct = $sqlQuery->distinct ? "DISTINCT " : ""; + if($sqlQuery->delete) { + $text = "DELETE "; + } else if($sqlQuery->select) { + $text = "SELECT $distinct" . implode(", ", $sqlQuery->select); + } + $text .= " FROM " . implode(" ", $sqlQuery->from); + + if($sqlQuery->where) $text .= " WHERE (" . $sqlQuery->getFilter(). ")"; + if($sqlQuery->groupby) $text .= " GROUP BY " . implode(", ", $sqlQuery->groupby); + if($sqlQuery->having) $text .= " HAVING ( " . implode(" ) AND ( ", $sqlQuery->having) . " )"; + if($sqlQuery->orderby) $text .= " ORDER BY " . $sqlQuery->orderby; + + if($sqlQuery->limit) { + $limit = $sqlQuery->limit; + // Pass limit as array or SQL string value + if(is_array($limit)) { + if(!array_key_exists('limit',$limit)) user_error('SQLQuery::limit(): Wrong format for $limit', E_USER_ERROR); + + if(isset($limit['start']) && is_numeric($limit['start']) && isset($limit['limit']) && is_numeric($limit['limit'])) { + // @todo MySQL specific LIMIT syntax + //$combinedLimit = (int)$limit['start'] . ',' . (int)$limit['limit']; + $combinedLimit = "$limit[limit] OFFSET $limit[start]"; + } elseif(isset($limit['limit']) && is_numeric($limit['limit'])) { + $combinedLimit = (int)$limit['limit']; + } else { + $combinedLimit = false; + } + if(!empty($combinedLimit)) $this->limit = $combinedLimit; + + } else { + $text .= " LIMIT " . $sqlQuery->limit; + } + } + + return $text; + } + } /** diff --git a/core/model/SQLQuery.php b/core/model/SQLQuery.php index 243d37d7b..3b870afa9 100755 --- a/core/model/SQLQuery.php +++ b/core/model/SQLQuery.php @@ -169,23 +169,7 @@ class SQLQuery extends Object { * @return SQLQuery This instance */ public function limit($limit) { - // Pass limit as array or SQL string value - if(is_array($limit)) { - if(!array_key_exists('limit',$limit)) user_error('SQLQuery::limit(): Wrong format for $limit', E_USER_ERROR); - - if(isset($limit['start']) && is_numeric($limit['start']) && isset($limit['limit']) && is_numeric($limit['limit'])) { - // @todo MySQL specific LIMIT syntax - $combinedLimit = (int)$limit['start'] . ',' . (int)$limit['limit']; - } elseif(isset($limit['limit']) && is_numeric($limit['limit'])) { - $combinedLimit = (int)$limit['limit']; - } else { - $combinedLimit = false; - } - } else { - $combinedLimit = $limit; - } - - if(!empty($combinedLimit)) $this->limit = $combinedLimit; + $this->limit = $limit; return $this; } @@ -392,22 +376,7 @@ class SQLQuery extends Object { * @return string */ function sql() { - if (!$this->from) return ''; - $distinct = $this->distinct ? "DISTINCT " : ""; - if($this->delete) { - $text = "DELETE "; - } else if($this->select) { - $text = "SELECT $distinct" . implode(", ", $this->select); - } - $text .= " FROM " . implode(" ", $this->from); - - if($this->where) $text .= " WHERE (" . $this->getFilter(). ")"; - if($this->groupby) $text .= " GROUP BY " . implode(", ", $this->groupby); - if($this->having) $text .= " HAVING ( " . implode(" ) AND ( ", $this->having) . " )"; - if($this->orderby) $text .= " ORDER BY " . $this->orderby; - if($this->limit) $text .= " LIMIT " . $this->limit; - - return $text; + return DB::getConn()->sqlQueryToString($this); } /**