_database_name = $options['database']; $this->_zasob_id = $options['zasob_id']; $this->_type = $options['type']; $this->_schema = (!empty($options['schema'])) ? $options['schema'] : null; unset($options['database']); unset($options['zasob_id']); parent::__construct($dsn, $username, $password, $options); $this->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); $this->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC); $this->setAttribute(PDO::ATTR_EMULATE_PREPARES, 1); // Default 1, fetch returns value types: |> 0 -> int, string, null |> 1 -> string, null } public function getDatabaseName() { return $this->_database_name; } public function getZasobId() { return $this->_zasob_id; } public function getType() { return strtolower($this->_type); } public function identifierQuote($identifier) { switch (strtolower($this->_type)) { // case 'pgsql': return $identifier; case 'pgsql': return "\"{$identifier}\""; // https://www.postgresql.org/docs/9.1/sql-syntax-lexical.html case 'mysql': return "`{$identifier}`"; } return $identifier; } public function tableNameQuote($tableName) { switch (strtolower($this->_type)) { case 'pgsql': return ($this->_schema) ? "{$this->_schema}.{$tableName}" : $tableName; // "'{$identifier}'"; case 'mysql': return "`{$tableName}`"; } return $tableName; } public function getTableStruct($tblName) {// TODO: mved to Core_Storage_* $sth = $this->prepare(" -- show fields from {$tblName} select cols.COLUMN_NAME as name , cols.DATA_TYPE as type , cols.COLUMN_TYPE as type_mysql , if('YES' = cols.IS_NULLABLE, 1, 0) as is_nullable , cols.COLUMN_DEFAULT as default_value , if(cols.COLUMN_DEFAULT is null, 1, 0) as default_is_null , cols.CHARACTER_MAXIMUM_LENGTH as max_length , cols.NUMERIC_PRECISION as num_precision , cols.NUMERIC_SCALE as num_scale , cols.CHARACTER_SET_NAME as char_encoding -- latin2 , cols.COLLATION_NAME as char_collation -- latin2_general_ci , cols.EXTRA as extra -- , cols.* from INFORMATION_SCHEMA.COLUMNS cols where cols.TABLE_SCHEMA = :db_name and cols.TABLE_NAME = :tbl_name "); $sth->bindValue(':db_name', $this->getDatabaseName(), PDO::PARAM_STR); $sth->bindValue(':tbl_name', $tblName, PDO::PARAM_STR); $sth->execute(); $structRaw = $sth->fetchAll(); if (empty($structRaw)) throw new Exception("Empty struct for table '{$tblName}'", 404); foreach ($structRaw as $field) { $struct[$field['name']] = $field; } return $struct; } public function assertTableStructXsd($tblName, $expectedStructXsd) { throw new Exception("Unimplemented - TODO!"); /* - `decimal(5,2)`: */ /* MySQL types: int tinyint smallint mediumint bigint decimal float double real => double date datetime timestamp time year char varchar text tinytext mediumtext longtext enum set bit boolean => `tinyint(1)` -- 0 or 1 serial => `bigint(20) unsigned` and unique key binary varbinary blob tinyblob mediumblob longblob geometry point linestring polygon multipoint multilinestring multipolygon geometrycollection */ } /* * TODO: update keys: * TODO: keys name may be different - try to find and connect with given schema? * TODO: remove old uniq keys? */ public function assertTableStruct($tblName, $expectedStruct, $params = array()) { // TODO: make backup for table? $expectedStruct = $this->_fixExpectedStruct($expectedStruct); //DBG::_(true, true, "fixedEpectedStruct", $expectedStruct, __CLASS__, __FUNCTION__, __LINE__); //DBG::_(true, true, "fixedEpectedStruct", $this->showCreateStruct($tblName, $expectedStruct, $params), __CLASS__, __FUNCTION__, __LINE__); $struct = $this->getTableStruct($tblName); $expectedFields = array();//array_keys($expectedStruct); foreach ($expectedStruct as $fldName => $fld) { if ('UNIQUE KEY' == $fld['type']) continue; if ('KEY' == $fld['type']) continue; $expectedFields[] = $fldName; } $currentFields = array_keys($struct); $missingFields = array_diff($expectedFields, $currentFields); DBG::_(true, true, "struct", $struct, __CLASS__, __FUNCTION__, __LINE__); DBG::_(true, true, "missingFields", $missingFields, __CLASS__, __FUNCTION__, __LINE__); foreach ($missingFields as $fldName) { $fld = $expectedStruct[$fldName]; DBG::_(true, true, "add missing field[{$fldName}]:", $fld, __CLASS__, __FUNCTION__, __LINE__); $sqlFieldStruct = $this->showTableStructField($fldName, $fld); if ($sqlFieldStruct) { $sqlAdd = "alter table {$tblName} add {$sqlFieldStruct}"; DBG::_(true, true, "sqlAdd", $sqlAdd, __CLASS__, __FUNCTION__, __LINE__); $this->exec($sqlAdd); } else { throw new Exception("Unimplemented type '{$fld['type']}': " . json_encode($fld)); } } $toUpdateFields = array_intersect($expectedFields, $currentFields); DBG::_(true, true, "toUpdateFields", $toUpdateFields, __CLASS__, __FUNCTION__, __LINE__); foreach ($toUpdateFields as $fldName) { $current = $struct[$fldName]; $expected = $expectedStruct[$fldName]; $needChange = false; $sqlFieldStruct = $this->showTableStructField($fldName, $expected); if (!$sqlFieldStruct) throw new Exception("Unimplemented type '{$expected['type']}' for field '{$fldName}': " . json_encode($expected)); $sqlChange = "alter table `{$tblName}` change `{$fldName}` {$sqlFieldStruct}"; DBG::_(true, true, "DBG: sqlChange", $sqlChange, __CLASS__, __FUNCTION__, __LINE__); //DBG::_(true, true, "TODO: update field[{$fldName}]:", array('expected'=>$expected,'current'=>$current), __CLASS__, __FUNCTION__, __LINE__); if ($current['type'] != $expected['type']) { throw new Exception("Unimplemented change field type from '{$current['type']}' to '{$expected['type']}' for field '{$fldName}': " . json_encode($expected)); } if ($current['is_nullable'] != $expected['is_nullable']) { $needChange = true; if ($current['is_nullable'] && !$expected['is_nullable']) { throw new Exception("Field struct needs change 'is_nullable' to false but this change is not implemented - field '{$fldName}': " . json_encode(array('expected'=>$expected,'current'=>$current))); } } if ($current['max_length'] != $expected['max_length']) { $needChange = true; if ($current['max_length'] > $expected['max_length']) { throw new Exception("Field struct needs decrease 'max_length' but this change is not implemented - field '{$fldName}': " . json_encode(array('expected'=>$expected,'current'=>$current))); } } if ($current['default_value'] !== $expected['default_value']) $needChange = true; if ($needChange) { DBG::_(true, true, "EXEC sqlChange ...", $sqlChange, __CLASS__, __FUNCTION__, __LINE__); $this->exec($sqlChange); } } } /* assert's that field struct has defined or throw exception if cannot set default value: ['type'] = 'varchar', 'int', ... (MySQL Types) ['is_nullable'] = true, false ['default_value'] = NULL or (string, numeric - based on type) default_value is not set when default_value == NULL and is_nullable == false ['default_is_null'] = true, false ['max_length'] = NULL, [0-...] // MySQL `CHARACTER_MAXIMUM_LENGTH` ['num_precision'] = NULL, [0-65] ['num_scale'] = NULL, [0-12] ['char_encoding'] = 'utf8', 'latin2', ... ['char_collation'] = 'utf8_general_ci', 'latin2_general_ci', ... ['extra'] = 'auto_increment', 'on update CURRENT_TIMESTAMP' ['values'] = null or array for enum and set TODO: validate - wrong related values. ex: {type: 'int', char_collation: 'latin2'} TODO: validate - not allowed value. ex: {type: 'xyz'} */ public function _fixExpectedStruct($expectedStruct) { $fixedStruct = array(); foreach ($expectedStruct as $fldName => $expected) { //DBG::_(true, true, "TODO: expected", $expected, __CLASS__, __FUNCTION__, __LINE__); if (!array_key_exists('type', $expected)) throw new Exception("Undefined type for field '{$fldName}'"); if (!array_key_exists('is_nullable', $expected)) { $expected['is_nullable'] = false; if (array_key_exists('default_value', $expected) && null === $expected['default_value']) { $expected['is_nullable'] = true; } } if (!array_key_exists('default_is_null', $expected)) $expected['default_is_null'] = false; if (!array_key_exists('default_value', $expected)) { $expected['default_value'] = null; // switch ($expected['type']) { // case 'char': // case 'varchar': $expected['default_value'] = ($expected['is_nullable'])? null : ''; break; // case 'tinyint': // case 'bigint': // case 'int': $expected['default_value'] = ($expected['is_nullable'])? null : 0; break; // } if ($expected['is_nullable'] && null === $expected['default_value']) { $expected['default_is_null'] = true; } } if (!array_key_exists('max_length', $expected)) { switch ($expected['type']) { case 'char': case 'varchar': $expected['max_length'] = 255; break; case 'binary': $expected['max_length'] = 255; break;// bainary(0) is possible - why? Cannot store values. case 'varbinary': $expected['max_length'] = 255; break; case 'binary': case 'varbinary': case 'bit': throw new Exception("Undefined max_length for field '{$fldName}' with type '{$expected['type']}'"); //case 'blob': $expected['max_length'] = 65535; break;// is set by engine //case 'tinyblob': $expected['max_length'] = 255; break;// is set by engine //case 'mediumblob': $expected['max_length'] = 16777215; break;// is set by engine //case 'longblob': $expected['max_length'] = 4294967295; break;// is set by engine //case 'text': $expected['max_length'] = 65535; break;// is set by engine //case 'tinytext': $expected['max_length'] = 255; break;// is set by engine //case 'mediumtext': $expected['max_length'] = 16777215; break;// is set by engine //case 'longtext': $expected['max_length'] = 4294967295; break;// is set by engine //case 'enum': $expected['max_length'] = 255; break;// is set by engine //case 'set': $expected['max_length'] = 255; break;// is set by engine default: $expected['max_length'] = null;//throw new Exception("Undefined max_length for field '{$fldName}'"); } } if (!array_key_exists('num_precision', $expected)) { switch ($expected['type']) { case 'int': $expected['num_precision'] = 10; break;// int(11) - +1 in type definition case 'tinyint': $expected['num_precision'] = 3; break;// int(4) case 'smallint': $expected['num_precision'] = 5; break;// int(6) case 'mediumint': $expected['num_precision'] = 7; break;// int(8) case 'bigint': $expected['num_precision'] = 19; break;// int(20) case 'decimal': $expected['num_precision'] = 10; break;// decimal(10,0) default: $expected['num_precision'] = null; } //throw new Exception("Undefined num_precision for field '{$fldName}'"); } if (!array_key_exists('num_scale', $expected)) { switch ($expected['type']) { case 'int': $expected['num_scale'] = 0; break; case 'tinyint': $expected['num_scale'] = 0; break; case 'smallint': $expected['num_scale'] = 0; break; case 'mediumint': $expected['num_scale'] = 0; break; case 'bigint': $expected['num_scale'] = 0; break; case 'decimal': $expected['num_scale'] = 0; break;// ex.: decimal(3,2); decimal(24,0) case 'double': $expected['num_scale'] = null; break;// ex.: double(10,4); double; double(17,0); case 'float': $expected['num_scale'] = null; break;// ex.: float(6,2); float; float(17,0); default: $expected['num_scale'] = null; } //throw new Exception("Undefined num_scale for field '{$fldName}'"); } if (!array_key_exists('char_encoding', $expected)) { switch ($expected['type']) { case 'char': case 'varchar': $expected['char_encoding'] = 'utf8'; break; case 'enum': case 'set': $expected['char_encoding'] = 'utf8'; break; case 'text': case 'tinytext': case 'mediumtext': case 'longtext': $expected['char_encoding'] = 'utf8'; break; default: $expected['char_encoding'] = null; } //throw new Exception("Undefined char_encoding for field '{$fldName}'"); } if (!array_key_exists('char_collation', $expected)) { switch ($expected['type']) { case 'char': case 'varchar': $expected['char_collation'] = 'utf8_general_ci'; break; case 'enum': case 'set': $expected['char_collation'] = 'utf8_general_ci'; break; case 'text': case 'tinytext': case 'mediumtext': case 'longtext': $expected['char_collation'] = 'utf8_general_ci'; break; default: $expected['char_collation'] = null; } //throw new Exception("Undefined char_collation for field '{$fldName}'"); } if (!array_key_exists('extra', $expected)) { $expected['extra'] = null;//throw new Exception("Undefined extra for field '{$fldName}'"); } if (!array_key_exists('values', $expected) || !is_array($expected['values']) || empty($expected['values'])) { switch ($expected['type']) { case 'enum': case 'set': throw new Exception("Undefined values for field '{$fldName}' with type '{$expected['type']}'"); default: $expected['values'] = null; } } $fixedStruct[$fldName] = $expected; } return $fixedStruct; } public function showCreateStruct($tblName, $struct, $params = array()) { // TODO: check database type, if == MySQL - fix construct $expectedStruct = $this->_fixExpectedStruct($struct); $linesSql = array(); $dbgSql = array(); foreach ($expectedStruct as $fldName => $fld) { $sqlFieldStruct = $this->showTableStructField($fldName, $fld); if ($sqlFieldStruct) { $linesSql[] = $sqlFieldStruct; } else { $dbgSql[] = "-- Unimplemented type '{$fld['type']}': " . json_encode($fld); } } $linesSql = implode("\n\t\t, ", $linesSql); $dbgSql = implode("\n\t\t", $dbgSql); $tblCharEncoding = V::get('char_encoding', 'utf8', $params); $structSql = << 0) { $typeParamsSql = "(" . ($fld['num_precision'] + 1) . ")"; } //if ($fld['num_scale']) $typeParamsSql = ",{$fld['num_scale']}"; return "`{$fldName}` {$fld['type']}{$typeParamsSql} {$nullSql} {$defaultSql}"; break; case 'decimal': $typeParamsSql = "{$fld['num_precision']},{$fld['num_scale']}"; return "`{$fldName}` {$fld['type']}({$typeParamsSql}) {$nullSql} {$defaultSql}"; break; case 'float': case 'double': case 'real': return "`{$fldName}` {$fld['type']} {$nullSql} {$defaultSql}"; break; case 'enum': case 'set': $typeParamsSql = "'" . implode("','", $fld['values']) . "'"; return "`{$fldName}` {$fld['type']}({$typeParamsSql}) {$nullSql} {$defaultSql}"; break; case 'bit': case 'binary': case 'varbinary': return "`{$fldName}` {$fld['type']}({$fld['max_length']}) {$nullSql} {$defaultSql}"; break; case 'boolean': case 'serial': case 'blob': case 'tinyblob': case 'mediumblob': case 'longblob': return "`{$fldName}` {$fld['type']} {$nullSql} {$defaultSql}"; break; case 'geometry': case 'point': case 'linestring': case 'polygon': case 'multipoint': case 'multilinestring': case 'multipolygon': case 'geometrycollection': return "`{$fldName}` {$fld['type']} {$nullSql} {$defaultSql}"; break; case 'UNIQUE KEY': $keyFieldsSql = "`" . implode("`,`", $fld['key_fields']) . "`"; return "UNIQUE KEY `{$fldName}` ({$keyFieldsSql})"; break; case 'KEY': $keyFieldsSql = "`" . implode("`,`", $fld['key_fields']) . "`"; return "KEY `{$fldName}` ({$keyFieldsSql})"; break; case 'PRIMARY KEY': $keyFieldsSql = "`" . implode("`,`", $fld['key_fields']) . "`"; return "PRIMARY KEY ({$keyFieldsSql})"; break; } return null; } public function fetchValue($sql, $values = []) { // for sql like `select count() from ...` $sth = $this->prepare($sql); if (!empty($values)) { $this->bindValues($sth, $values); DBG::log($this->getRawSql($sth), 'sql'); } else { DBG::log($sql, 'sql'); } $sth->execute(); return $sth->fetchColumn(); } public function fetchValuesList($sql, $values = []) { // for sql like `select ID from ...` @returns array of ID return array_map(function ($row) { return reset($row); }, $this->fetchAll($sql, $values)); } public function fetchValuesListByKey($sql, $key, $values = []) { // for sql like `select ID from ...` @returns array of ID return array_map(function ($row) { return reset($row); }, $this->fetchAllByKey($sql, $key, $values)); } public function fetchFirst($sql, $values = []) { // fetch only first row $sth = $this->prepare($sql); if (!empty($values)) { $this->bindValues($sth, $values); DBG::log($this->getRawSql($sth), 'sql'); } else { DBG::log($sql, 'sql'); } $sth->execute(); return $sth->fetch(); } public function fetchFirstNoLog($sql, $values = []) { // fetch only first row - used in User::getID() required in DBG $sth = $this->prepare($sql); if (!empty($values)) { $this->bindValues($sth, $values); } $sth->execute(); return $sth->fetch(); } public function fetchAll($sql, $values = []) { $sth = $this->prepare($sql); if (!empty($values)) { $this->bindValues($sth, $values); DBG::log($this->getRawSql($sth), 'sql'); } else { DBG::log($sql, 'sql'); } $sth->execute(); return $sth->fetchAll(); } public function fetchAllByKey($sql, $key = 'ID', $values = []) { $rowsByKey = array(); $sth = $this->prepare($sql); if (!empty($values)) { $this->bindValues($sth, $values); DBG::log($this->getRawSql($sth), 'sql'); } else { DBG::log($sql, 'sql'); } $sth->execute(); $rows = $sth->fetchAll(); foreach ($rows as $row) { $keyRow = V::get($key, null, $row); $rowsByKey[$keyRow] = $row; } return $rowsByKey; } public function bindValues($sth, $values) { foreach ($values as $name => $value) { $val = $value; $type = PDO::PARAM_STR; if (is_array($value)) { $val = $value[0]; if (count($value) > 1) { $type = $value[1]; } } $sth->bindValue($name, $val, $type); if (!isset($sth->bindedValues)) $sth->bindedValues = array(); $sth->bindedValues[$name] = array($val, $type); } } public function getRawSql($sth, $values = array()) { $sql = $sth->queryString; $params = array(); if (!empty($sth->bindedValues)) { foreach ($sth->bindedValues as $name => $value) { $params[$name] = array($value[0], $value[1]); } } foreach ($values as $name => $value) { $val = $value; $type = PDO::PARAM_STR; if (is_array($value)) { $val = $value[0]; if (count($value) > 1) { $type = $value[1]; } } $params[$name] = array($val, $type); } if (!empty($params)) { foreach ($params as $name => $val) { $outValue = $val[0]; if (PDO::PARAM_STR == $val[1]) $outValue = "'{$outValue}'"; $sql = str_replace((':' === $name[0] ? $name : ":{$name}"), $outValue, $sql); } } return $sql; } public function insert($tableName, $item, $sqlSchema = []) {// @returns int last inserted id if (empty($tableName)) throw new Exception("Missing table name"); if (!is_array($item)) throw new Exception("Missing item"); $sqlFields = []; $sqlValues = []; foreach ($item as $field => $val) { $sqlFields[] = $this->identifierQuote($field); $sqlValues[] = $this->convertValueToSqlSafe($val, V::get($field, null, $sqlSchema)); } $sqlTableName = $this->tableNameQuote($tableName); $sql = " insert into {$sqlTableName} (" . implode(", ", $sqlFields) . ") values (" . implode(", ", $sqlValues) . ") "; $this->execSql($sql); return $this->lastInsertId(); } public function insertIgnore($tableName, $item, $sqlSchema = []) {// @returns int last inserted id if (empty($tableName)) throw new Exception("Missing table name"); if (!is_array($item)) throw new Exception("Missing item"); $sqlFields = []; $sqlValues = []; foreach ($item as $field => $val) { $sqlFields[] = $this->identifierQuote($field); $sqlValues[] = $this->convertValueToSqlSafe($val, V::get($field, null, $sqlSchema)); } $sqlTableName = $this->tableNameQuote($tableName); $sql = " insert ignore into {$sqlTableName} (" . implode(", ", $sqlFields) . ") values (" . implode(", ", $sqlValues) . ") "; $this->execSql($sql); return $this->lastInsertId(); } public function update($tableName, $primaryKeyName, $primaryKey, $item, $sqlSchema = []) {// @returns int affected rows if (empty($tableName)) throw new Exception("Missing table name"); if (empty($primaryKeyName)) throw new Exception("Missing primaryKey name"); if (empty($primaryKey)) throw new Exception("Missing primaryKey"); if (empty($item) || !is_array($item)) throw new Exception("Missing item"); $sqlPrimaryKey = $this->quote($primaryKey, PDO::PARAM_STR); $sqlUpdateSet = []; foreach ($item as $field => $val) { $sqlUpdateSet[] = $this->identifierQuote($field) . " = " . $this->convertValueToSqlSafe($val, V::get($field, null, $sqlSchema)); } $sqlTableName = $this->identifierQuote($tableName); $sqlPkName = $this->identifierQuote($primaryKeyName); $sql = " update {$sqlTableName} set " . implode("\n , ", $sqlUpdateSet) . " where {$sqlPkName} = {$sqlPrimaryKey} "; return $this->execSql($sql); } // '@insert' => [ // 'A_RECORD_CREATE_AUTHOR' => User::getLogin(), // 'A_RECORD_CREATE_DATE' => 'NOW()', // ], // '@update' => [ // 'A_RECORD_UPDATE_AUTHOR' => User::getLogin(), // 'A_RECORD_UPDATE_DATE' => 'NOW()', // ] public function insertOrUpdate($tableName, $item, $sqlSchema = []) { if (empty($tableName)) throw new Exception("Missing table name"); if (empty($item) || !is_array($item)) throw new Exception("Missing item"); $sqlFields = []; $sqlValues = []; $sqlUpdateSet = []; foreach ($item as $field => $val) { if ('@insert' == $field) continue; if ('@update' == $field) continue; $sqlVal = $this->convertValueToSqlSafe($val, V::get($field, null, $sqlSchema)); $sqlFields[] = "`{$field}`"; $sqlValues[] = $sqlVal; $sqlUpdateSet[] = "`{$field}` = {$sqlVal}"; } if (!empty($item['@insert'])) { foreach ($item['@insert'] as $field => $val) { $sqlFields[] = "`{$field}`"; $sqlValues[] = $this->convertValueToSqlSafe($val, V::get($field, null, $sqlSchema)); } } if (!empty($item['@update'])) { foreach ($item['@update'] as $field => $val) { $sqlUpdateSet[] = "`{$field}` = " . $this->convertValueToSqlSafe($val, V::get($field, null, $sqlSchema)); } } $sql = " insert into `{$tableName}` (" . implode(", ", $sqlFields) . ") values (" . implode(", ", $sqlValues) . ") "; if (!empty($sqlUpdateSet)) $sql .= " on duplicate key update " . implode(", ", $sqlUpdateSet); $affected = $this->execSql($sql); return true; // return $affected; // $this->lastInsertId(); } public function convertValueToSqlSafe($value, $xsdType = null) { if ('NOW()' === $value) return 'NOW()'; else if (NULL === $value) return 'NULL'; else if ('GeomFromText' == substr($value, 0, strlen('GeomFromText'))) return $value; else return $this->quote($value, PDO::PARAM_STR);// TODO: use $sqlSchema if set } public function execSql($sql, $values = []) { try { if (empty($values)) { DBG::log($sql, 'sql'); $retAffected = $this->exec($sql); } else { $sth = $this->prepare($sql); if (!empty($values)) { $this->bindValues($sth, $values); DBG::log($this->getRawSql($sth), 'sql'); } else { DBG::log($sql, 'sql'); } $sth->execute(); $retAffected = $sth->rowCount(); } } catch (Exception $e) { DBG::log($e); $dbType = $this->getType(); $duplicateRegexp = "/^SQLSTATE\[23000\]\: Integrity constraint violation: 1062 Duplicate entry '([0-9]+)' for key '(.*)'/"; if ('mysql' == $dbType && preg_match_all($duplicateRegexp, $e->getMessage(), $matches) > 0) { DBG::log(['matches'=>$matches,'msg'=>$e->getMessage(),'regex'=>$duplicateRegexp], 'array', '$matches duplicate test'); throw new MysqlDuplicateEntryException("Duplicate entry '{$matches[1][0]}'", 1062, null, $matches[1][0], $matches[2][0]); } else { throw $e; } } return $retAffected; } public function getBlob($tableName, $fieldName, $pkField, $primaryKey) { if (empty($tableName)) throw new Exception("Missing tableName in PDO::getBlob"); if (empty($fieldName)) throw new Exception("Missing fieldName in PDO::getBlob"); if (empty($pkField)) throw new Exception("Missing pkField in PDO::getBlob"); if (empty($primaryKey)) throw new Exception("Missing primaryKey in PDO::getBlob"); $dbType = $this->getType(); switch ($dbType) { case 'mysql': { $sql = " select `{$fieldName}` from `{$tableName}` where `{$pkField}` = :pk limit 1 "; $sth = $this->prepare($sql); $sth->bindValue(':pk', $primaryKey, PDO::PARAM_STR); $sth->execute(); $sth->bindColumn(1, $content, PDO::PARAM_LOB); $sth->fetch(); return $content; } break; default: throw new Exception("Not implemented getBlob for database type '{$dbType}'"); } } public function queryNotBuffered($query) { if ($bufferedQuery = $this->getAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY)) DB::getPDO()->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, false); $return = parent::query($query); if ($bufferedQuery) DB::getPDO()->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, false); return $return; } } class MysqlDuplicateEntryException extends DatabaseDuplicateEntryException {} class DatabaseDuplicateEntryException extends Exception { public function __construct($message, $code = 0, Exception $previous = null, $sqlPrimaryKey = '', $sqlKeyName = '') { $this->keyName = $sqlKeyName; $this->primaryKey = $sqlPrimaryKey; parent::__construct($message, $code, $previous); } }