|
@@ -1,6 +1,7 @@
|
|
|
<?php
|
|
<?php
|
|
|
|
|
|
|
|
Lib::loadClass('DBG');
|
|
Lib::loadClass('DBG');
|
|
|
|
|
+Lib::loadClass('Core_TypeFactory'); // Core_TypeFactory::make, Core_iPdoSqlValue::toSqlQuote
|
|
|
|
|
|
|
|
class Core_Pdo extends PDO {
|
|
class Core_Pdo extends PDO {
|
|
|
|
|
|
|
@@ -36,26 +37,13 @@ class Core_Pdo extends PDO {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
public function identifierQuote($identifier) {
|
|
public function identifierQuote($identifier) {
|
|
|
- switch (strtolower($this->_type)) {
|
|
|
|
|
- // case 'pgsql': return $identifier;
|
|
|
|
|
- case 'pgsql': return $this->pgsqlIdentifierQuote($identifier);
|
|
|
|
|
- case 'mysql': return "`{$identifier}`";
|
|
|
|
|
- }
|
|
|
|
|
- return $identifier;
|
|
|
|
|
- }
|
|
|
|
|
- function pgsqlIdentifierQuote($identifier) {
|
|
|
|
|
- if (false !== strpos($identifier, '.')) {
|
|
|
|
|
- return implode('.', array_map(function ($token) {
|
|
|
|
|
- return "\"{$token}\"";
|
|
|
|
|
- }, explode('.', $identifier)));
|
|
|
|
|
- }
|
|
|
|
|
- return "\"{$identifier}\""; // https://www.postgresql.org/docs/9.1/sql-syntax-lexical.html
|
|
|
|
|
|
|
+ return DB::identifierQuote($this->_type, $identifier);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
public function tableNameQuote($tableName) {
|
|
public function tableNameQuote($tableName) {
|
|
|
switch (strtolower($this->_type)) {
|
|
switch (strtolower($this->_type)) {
|
|
|
- case 'pgsql': return ($this->_schema) ? "{$this->_schema}.{$tableName}" : $tableName; // "'{$identifier}'";
|
|
|
|
|
- case 'mysql': return "`{$tableName}`";
|
|
|
|
|
|
|
+ case 'pgsql': return DB::identifierQuote($this->_type, ($this->_schema) ? "{$this->_schema}.{$tableName}" : $tableName);
|
|
|
|
|
+ case 'mysql': return DB::identifierQuote($this->_type, $tableName);
|
|
|
}
|
|
}
|
|
|
return $tableName;
|
|
return $tableName;
|
|
|
}
|
|
}
|
|
@@ -465,6 +453,12 @@ EOF_STRUCT_MYSQL;
|
|
|
}, $this->fetchAllByKey($sql, $key, $values));
|
|
}, $this->fetchAllByKey($sql, $key, $values));
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ public function fetchKeyValueList($sql, $keyField, $valueField, $values = []) { // for sql like `select key, val from ...` @returns assoc array key => val
|
|
|
|
|
+ return array_map(function ($row) use ($valueField) {
|
|
|
|
|
+ return V::get($valueField, '', $row);
|
|
|
|
|
+ }, $this->fetchAllByKey($sql, $keyField, $values));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
public function fetchFirst($sql, $values = []) { // fetch only first row
|
|
public function fetchFirst($sql, $values = []) { // fetch only first row
|
|
|
$sth = $this->prepare($sql);
|
|
$sth = $this->prepare($sql);
|
|
|
if (!empty($values)) {
|
|
if (!empty($values)) {
|
|
@@ -565,16 +559,16 @@ EOF_STRUCT_MYSQL;
|
|
|
public function insert($tableName, $item, $sqlSchema = []) {// @returns int last inserted id
|
|
public function insert($tableName, $item, $sqlSchema = []) {// @returns int last inserted id
|
|
|
if (empty($tableName)) throw new Exception("Missing table name");
|
|
if (empty($tableName)) throw new Exception("Missing table name");
|
|
|
if (!is_array($item)) throw new Exception("Missing item");
|
|
if (!is_array($item)) throw new Exception("Missing item");
|
|
|
- $sqlFields = [];
|
|
|
|
|
- $sqlValues = [];
|
|
|
|
|
|
|
+ $sqlListFields = [];
|
|
|
|
|
+ $sqlListValues = [];
|
|
|
foreach ($item as $field => $val) {
|
|
foreach ($item as $field => $val) {
|
|
|
- $sqlFields[] = $this->identifierQuote($field);
|
|
|
|
|
- $sqlValues[] = $this->convertValueToSqlSafe($val, V::get($field, null, $sqlSchema));
|
|
|
|
|
|
|
+ $sqlListFields[] = $this->identifierQuote($field);
|
|
|
|
|
+ $sqlListValues[] = $this->convertValueToSqlSafe($val, V::get($field, null, $sqlSchema));
|
|
|
}
|
|
}
|
|
|
$sqlTableName = $this->tableNameQuote($tableName);
|
|
$sqlTableName = $this->tableNameQuote($tableName);
|
|
|
$sql = "
|
|
$sql = "
|
|
|
- insert into {$sqlTableName} (" . implode(", ", $sqlFields) . ")
|
|
|
|
|
- values (" . implode(", ", $sqlValues) . ")
|
|
|
|
|
|
|
+ insert into {$sqlTableName} (" . implode(", ", $sqlListFields) . ")
|
|
|
|
|
+ values (" . implode(", ", $sqlListValues) . ")
|
|
|
";
|
|
";
|
|
|
$this->execSql($sql);
|
|
$this->execSql($sql);
|
|
|
return $this->lastInsertId();
|
|
return $this->lastInsertId();
|
|
@@ -583,16 +577,16 @@ EOF_STRUCT_MYSQL;
|
|
|
public function insertIgnore($tableName, $item, $sqlSchema = []) {// @returns int last inserted id
|
|
public function insertIgnore($tableName, $item, $sqlSchema = []) {// @returns int last inserted id
|
|
|
if (empty($tableName)) throw new Exception("Missing table name");
|
|
if (empty($tableName)) throw new Exception("Missing table name");
|
|
|
if (!is_array($item)) throw new Exception("Missing item");
|
|
if (!is_array($item)) throw new Exception("Missing item");
|
|
|
- $sqlFields = [];
|
|
|
|
|
- $sqlValues = [];
|
|
|
|
|
|
|
+ $sqlListFields = [];
|
|
|
|
|
+ $sqlListValues = [];
|
|
|
foreach ($item as $field => $val) {
|
|
foreach ($item as $field => $val) {
|
|
|
- $sqlFields[] = $this->identifierQuote($field);
|
|
|
|
|
- $sqlValues[] = $this->convertValueToSqlSafe($val, V::get($field, null, $sqlSchema));
|
|
|
|
|
|
|
+ $sqlListFields[] = $this->identifierQuote($field);
|
|
|
|
|
+ $sqlListValues[] = $this->convertValueToSqlSafe($val, V::get($field, null, $sqlSchema));
|
|
|
}
|
|
}
|
|
|
$sqlTableName = $this->tableNameQuote($tableName);
|
|
$sqlTableName = $this->tableNameQuote($tableName);
|
|
|
$sql = "
|
|
$sql = "
|
|
|
- insert ignore into {$sqlTableName} (" . implode(", ", $sqlFields) . ")
|
|
|
|
|
- values (" . implode(", ", $sqlValues) . ")
|
|
|
|
|
|
|
+ insert ignore into {$sqlTableName} (" . implode(", ", $sqlListFields) . ")
|
|
|
|
|
+ values (" . implode(", ", $sqlListValues) . ")
|
|
|
";
|
|
";
|
|
|
$this->execSql($sql);
|
|
$this->execSql($sql);
|
|
|
return $this->lastInsertId();
|
|
return $this->lastInsertId();
|
|
@@ -603,21 +597,24 @@ EOF_STRUCT_MYSQL;
|
|
|
if (empty($primaryKeyName)) throw new Exception("Missing primaryKey name");
|
|
if (empty($primaryKeyName)) throw new Exception("Missing primaryKey name");
|
|
|
if (empty($primaryKey)) throw new Exception("Missing primaryKey");
|
|
if (empty($primaryKey)) throw new Exception("Missing primaryKey");
|
|
|
if (empty($item) || !is_array($item)) throw new Exception("Missing item");
|
|
if (empty($item) || !is_array($item)) throw new Exception("Missing item");
|
|
|
- $sqlPrimaryKey = $this->quote($primaryKey, PDO::PARAM_STR);
|
|
|
|
|
$sqlUpdateSet = [];
|
|
$sqlUpdateSet = [];
|
|
|
foreach ($item as $field => $val) {
|
|
foreach ($item as $field => $val) {
|
|
|
- $sqlUpdateSet[] = $this->identifierQuote($field) . " = " . $this->convertValueToSqlSafe($val, V::get($field, null, $sqlSchema));
|
|
|
|
|
|
|
+ $sqlFieldName = $this->identifierQuote($field);
|
|
|
|
|
+ $sqlValue = $this->convertValueToSqlSafe($val, V::get($field, null, $sqlSchema));
|
|
|
|
|
+ $sqlUpdateSet[] = "{$sqlFieldName} = {$sqlValue}";
|
|
|
}
|
|
}
|
|
|
- $sqlTableName = $this->identifierQuote($tableName);
|
|
|
|
|
|
|
+ $sqlTableName = $this->tableNameQuote($tableName);
|
|
|
$sqlPkName = $this->identifierQuote($primaryKeyName);
|
|
$sqlPkName = $this->identifierQuote($primaryKeyName);
|
|
|
|
|
+ $sqlPkValue = $this->quote($primaryKey, PDO::PARAM_STR);
|
|
|
$sql = "
|
|
$sql = "
|
|
|
update {$sqlTableName}
|
|
update {$sqlTableName}
|
|
|
set " . implode("\n , ", $sqlUpdateSet) . "
|
|
set " . implode("\n , ", $sqlUpdateSet) . "
|
|
|
- where {$sqlPkName} = {$sqlPrimaryKey}
|
|
|
|
|
|
|
+ where {$sqlPkName} = {$sqlPkValue}
|
|
|
";
|
|
";
|
|
|
return $this->execSql($sql);
|
|
return $this->execSql($sql);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ // fieldName => fieldValue, // will update if duplicate key (sql: `on duplicate key update`)
|
|
|
// '@insert' => [
|
|
// '@insert' => [
|
|
|
// 'A_RECORD_CREATE_AUTHOR' => User::getLogin(),
|
|
// 'A_RECORD_CREATE_AUTHOR' => User::getLogin(),
|
|
|
// 'A_RECORD_CREATE_DATE' => 'NOW()',
|
|
// 'A_RECORD_CREATE_DATE' => 'NOW()',
|
|
@@ -629,31 +626,37 @@ EOF_STRUCT_MYSQL;
|
|
|
public function insertOrUpdate($tableName, $item, $sqlSchema = []) {
|
|
public function insertOrUpdate($tableName, $item, $sqlSchema = []) {
|
|
|
if (empty($tableName)) throw new Exception("Missing table name");
|
|
if (empty($tableName)) throw new Exception("Missing table name");
|
|
|
if (empty($item) || !is_array($item)) throw new Exception("Missing item");
|
|
if (empty($item) || !is_array($item)) throw new Exception("Missing item");
|
|
|
- $sqlFields = [];
|
|
|
|
|
- $sqlValues = [];
|
|
|
|
|
|
|
+ $sqlListFields = [];
|
|
|
|
|
+ $sqlListValues = [];
|
|
|
$sqlUpdateSet = [];
|
|
$sqlUpdateSet = [];
|
|
|
foreach ($item as $field => $val) {
|
|
foreach ($item as $field => $val) {
|
|
|
if ('@insert' == $field) continue;
|
|
if ('@insert' == $field) continue;
|
|
|
if ('@update' == $field) continue;
|
|
if ('@update' == $field) continue;
|
|
|
- $sqlVal = $this->convertValueToSqlSafe($val, V::get($field, null, $sqlSchema));
|
|
|
|
|
- $sqlFields[] = "`{$field}`";
|
|
|
|
|
- $sqlValues[] = $sqlVal;
|
|
|
|
|
- $sqlUpdateSet[] = "`{$field}` = {$sqlVal}";
|
|
|
|
|
|
|
+ $sqlFieldName = $this->identifierQuote($field);
|
|
|
|
|
+ $sqlValue = $this->convertValueToSqlSafe($val, V::get($field, null, $sqlSchema));
|
|
|
|
|
+ $sqlListFields[] = $sqlFieldName;
|
|
|
|
|
+ $sqlListValues[] = $sqlValue;
|
|
|
|
|
+ $sqlUpdateSet[] = "{$sqlFieldName} = {$sqlValue}";
|
|
|
}
|
|
}
|
|
|
if (!empty($item['@insert'])) {
|
|
if (!empty($item['@insert'])) {
|
|
|
foreach ($item['@insert'] as $field => $val) {
|
|
foreach ($item['@insert'] as $field => $val) {
|
|
|
- $sqlFields[] = "`{$field}`";
|
|
|
|
|
- $sqlValues[] = $this->convertValueToSqlSafe($val, V::get($field, null, $sqlSchema));
|
|
|
|
|
|
|
+ $sqlFieldName = $this->identifierQuote($field);
|
|
|
|
|
+ $sqlValue = $this->convertValueToSqlSafe($val, V::get($field, null, $sqlSchema));
|
|
|
|
|
+ $sqlListFields[] = $sqlFieldName;
|
|
|
|
|
+ $sqlListValues[] = $sqlValue;
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
if (!empty($item['@update'])) {
|
|
if (!empty($item['@update'])) {
|
|
|
foreach ($item['@update'] as $field => $val) {
|
|
foreach ($item['@update'] as $field => $val) {
|
|
|
- $sqlUpdateSet[] = "`{$field}` = " . $this->convertValueToSqlSafe($val, V::get($field, null, $sqlSchema));
|
|
|
|
|
|
|
+ $sqlFieldName = $this->identifierQuote($field);
|
|
|
|
|
+ $sqlValue = $this->convertValueToSqlSafe($val, V::get($field, null, $sqlSchema));
|
|
|
|
|
+ $sqlUpdateSet[] = "{$sqlFieldName} = {$sqlValue}";
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
+ $sqlTableName = $this->tableNameQuote($tableName);
|
|
|
$sql = "
|
|
$sql = "
|
|
|
- insert into `{$tableName}` (" . implode(", ", $sqlFields) . ")
|
|
|
|
|
- values (" . implode(", ", $sqlValues) . ")
|
|
|
|
|
|
|
+ insert into {$sqlTableName} (" . implode(", ", $sqlListFields) . ")
|
|
|
|
|
+ values (" . implode(", ", $sqlListValues) . ")
|
|
|
";
|
|
";
|
|
|
if (!empty($sqlUpdateSet)) $sql .= " on duplicate key update " . implode(", ", $sqlUpdateSet);
|
|
if (!empty($sqlUpdateSet)) $sql .= " on duplicate key update " . implode(", ", $sqlUpdateSet);
|
|
|
$affected = $this->execSql($sql);
|
|
$affected = $this->execSql($sql);
|
|
@@ -661,10 +664,15 @@ EOF_STRUCT_MYSQL;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
public function convertValueToSqlSafe($value, $xsdType = null) {
|
|
public function convertValueToSqlSafe($value, $xsdType = null) {
|
|
|
|
|
+ if (is_object($value)) {
|
|
|
|
|
+ if ($value instanceof Core_iPdoSqlValue) return $value->toSqlQuote($this); // TODO: use P5Type to convert to sql value
|
|
|
|
|
+ throw new Exception("DB Error: convertValueToSqlSafe value not implementing iPdoSqlValue '" . get_class($value) . "'");
|
|
|
|
|
+ }
|
|
|
if ('NOW()' === $value) return 'NOW()';
|
|
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
|
|
|
|
|
|
|
+ if (NULL === $value) return 'NULL';
|
|
|
|
|
+ if ('GeomFromText' == substr($value, 0, strlen('GeomFromText'))) return $value; // TODO: convert to Core_iPdoSqlValue, security
|
|
|
|
|
+ if ('FROM_UNIXTIME' == substr($value, 0, strlen('FROM_UNIXTIME'))) return $value; // TODO: convert to Core_iPdoSqlValue, security
|
|
|
|
|
+ return $this->quote($value, PDO::PARAM_STR); // TODO: use $sqlSchema if set
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
public function execSql($sql, $values = []) {
|
|
public function execSql($sql, $values = []) {
|
|
@@ -729,6 +737,18 @@ EOF_STRUCT_MYSQL;
|
|
|
return $return;
|
|
return $return;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+
|
|
|
|
|
+ public function tryHandleException($handler, $methodName, $args) { // try again on exception
|
|
|
|
|
+ try {
|
|
|
|
|
+ return call_user_func_array([ $this, $methodName ], $args);
|
|
|
|
|
+ } catch (Exception $e) {
|
|
|
|
|
+ DBG::log("DBG:PDO->tryHandleException Exception trying to fix using handler ...");
|
|
|
|
|
+ DBG::log($e);
|
|
|
|
|
+ $handler($e);
|
|
|
|
|
+ return call_user_func_array([ $this, $methodName ], $args);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|