_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);
}
}