Commit 93f87e41 authored by Laurent Lécluse's avatar Laurent Lécluse
Browse files

Gestion des CLOB par l'import

parent 55b0ed2b
......@@ -5,13 +5,23 @@ namespace UnicaenImport\Entity\Schema;
/**
*
*
*
* @author Laurent LÉCLUSE <laurent.lecluse at unicaen.fr>
*/
class Column
{
/**
* @var string
*/
public $table;
/**
* @var string
*/
public $name;
/**
* Type de données
*
......@@ -61,4 +71,11 @@ class Column
*/
public $importActif;
function __toString()
{
return $this->name;
}
}
\ No newline at end of file
<?php
namespace UnicaenImport\Service;
use UnicaenImport\Entity\Schema\Column;
use UnicaenImport\Exception\Exception;
use UnicaenImport\Entity\Differentiel\Query;
use UnicaenImport\Options\Traits\ModuleOptionsAwareTrait;
......@@ -111,7 +112,7 @@ class QueryGeneratorService extends AbstractService
$ignoreFields = $this->escape(implode(',', $ignoreFields));
}
$sql = "BEGIN ".$this->getPackage().".SET_CURRENT_USER($userId);".$this->getPackage().".$procName($conditions,$ignoreFields); END;";
$sql = "BEGIN " . $this->getPackage() . ".SET_CURRENT_USER($userId);" . $this->getPackage() . ".$procName($conditions,$ignoreFields); END;";
try {
$this->getEntityManager()->getConnection()->exec($sql);
} catch (\Doctrine\DBAL\DBALException $e) {
......@@ -140,7 +141,7 @@ class QueryGeneratorService extends AbstractService
$errors = [];
$lastLogId = $this->getLastLogId();
$sql = "BEGIN ".$this->getPackage().".SET_CURRENT_USER($userId);".$this->getPackage()."." . $this->escapeKW('MAJ_' . $tableName) . "; END;";
$sql = "BEGIN " . $this->getPackage() . ".SET_CURRENT_USER($userId);" . $this->getPackage() . "." . $this->escapeKW('MAJ_' . $tableName) . "; END;";
try {
$this->getEntityManager()->getConnection()->exec($sql);
} catch (\Doctrine\DBAL\DBALException $e) {
......@@ -201,7 +202,7 @@ class QueryGeneratorService extends AbstractService
*/
public function getSqlCriterion($tableName)
{
$sql = 'SELECT '.$this->getPackage().'.GET_SQL_CRITERION(' . $this->escape($tableName) . ',\'\') res FROM DUAL';
$sql = 'SELECT ' . $this->getPackage() . '.GET_SQL_CRITERION(' . $this->escape($tableName) . ',\'\') res FROM DUAL';
$stmt = $this->getEntityManager()->getConnection()->executeQuery($sql);
if ($r = $stmt->fetch()) {
......@@ -285,7 +286,7 @@ class QueryGeneratorService extends AbstractService
*/
protected function getPackageDeclaration()
{
$sql = "SELECT TEXT FROM USER_SOURCE WHERE NAME = '".$this->getPackage()."' AND type = 'PACKAGE'";
$sql = "SELECT TEXT FROM USER_SOURCE WHERE NAME = '" . $this->getPackage() . "' AND type = 'PACKAGE'";
$result = $this->query($sql, [], 'TEXT');
return implode("", $result);
......@@ -300,7 +301,7 @@ class QueryGeneratorService extends AbstractService
*/
protected function getPackageBody()
{
$sql = "SELECT TEXT FROM USER_SOURCE WHERE NAME = '".$this->getPackage()."' AND type = 'PACKAGE BODY'";
$sql = "SELECT TEXT FROM USER_SOURCE WHERE NAME = '" . $this->getPackage() . "' AND type = 'PACKAGE BODY'";
$result = $this->query($sql, [], 'TEXT');
return implode("", $result);
......@@ -361,10 +362,13 @@ class QueryGeneratorService extends AbstractService
return $result;
}
/**
* Teste que la séquence correspondant à la table spécifiée existe bien.
*
* @param string $table
*
* @throws \Doctrine\DBAL\DBALException En cas d'erreur en BDD
* @throws Exception Si la séquence n'existe pas.
*
......@@ -374,13 +378,13 @@ class QueryGeneratorService extends AbstractService
$sql = 'SELECT COUNT(*) SEQ_FOUND FROM USER_SEQUENCES WHERE SEQUENCE_NAME = :sequenceName';
$sequenceName = strtoupper($table) . '_ID_SEQ';
$stmt = $this->getEntityManager()->getConnection()->executeQuery($sql, ['sequenceName' => $sequenceName]);
$stmt = $this->getEntityManager()->getConnection()->executeQuery($sql, ['sequenceName' => $sequenceName]);
$sequenceFound = false;
if ($r = $stmt->fetch()) {
$sequenceFound = (bool)(int)$r['SEQ_FOUND'];
}
if (! $sequenceFound) {
if (!$sequenceFound) {
throw new Exception("La séquence '$sequenceName' doit être créée (ex: CREATE SEQUENCE $sequenceName)");
}
}
......@@ -467,11 +471,11 @@ class QueryGeneratorService extends AbstractService
$joinCond = ' AND S.' . self::ANNEE_COLUMN_NAME . ' = d.' . self::ANNEE_COLUMN_NAME;
}
// destruction ssi dans l'année d'import courante
$delCond = ' AND d.' . self::ANNEE_COLUMN_NAME . ' = '.$this->getPackage().'.get_current_annee';
$delCond = ' AND d.' . self::ANNEE_COLUMN_NAME . ' = ' . $this->getPackage() . '.get_current_annee';
} else {
// on recherche si la table dépend d'une table qui, elle, serait annualisée
foreach ($schema as $columnName => $column) {
/* @var $column \Import\Entity\Schema\Column */
/* @var $column \UnicaenImport\Entity\Schema\Column */
if (!empty($column->refTableName)) {
$refSchema = $this->getServiceSchema()->getSchema($column->refTableName);
if (!empty($refSchema) && array_key_exists(self::ANNEE_COLUMN_NAME, $refSchema)) {
......@@ -479,7 +483,7 @@ class QueryGeneratorService extends AbstractService
// Donc, on utilise la table référente
$depJoin = "\n LEFT JOIN " . $column->refTableName . " rt ON rt." . $column->refColumnName . " = d." . $columnName;
// destruction ssi dans l'année d'import courante de la table référente
$delCond = ' AND rt.' . self::ANNEE_COLUMN_NAME . ' = '.$this->getPackage().'.get_current_annee';
$delCond = ' AND rt.' . self::ANNEE_COLUMN_NAME . ' = ' . $this->getPackage() . '.get_current_annee';
break;
/* on stoppe à la première table contenant une année.
......@@ -493,7 +497,10 @@ class QueryGeneratorService extends AbstractService
// on génère ensuite la bonne requête !!!
$cols = $this->getCols($tableName);
$sql = "CREATE OR REPLACE FORCE VIEW V_DIFF_$tableName AS
foreach ($cols as $id => $col) {
$cols[$id] = $schema[$col];
}
$sql = "CREATE OR REPLACE FORCE VIEW V_DIFF_$tableName AS
select diff.* from (SELECT
COALESCE( D.id, S.id ) id,
COALESCE( S.source_id, D.source_id ) source_id,
......@@ -503,8 +510,20 @@ CASE
WHEN S.source_code IS NOT NULL AND D.source_code IS NOT NULL AND (D.histo_destruction IS NULL OR D.histo_destruction > SYSDATE) THEN 'update'
WHEN S.source_code IS NULL AND D.source_code IS NOT NULL AND (D.histo_destruction IS NULL OR D.histo_destruction > SYSDATE)$delCond THEN 'delete'
WHEN S.source_code IS NOT NULL AND D.source_code IS NOT NULL AND D.histo_destruction IS NOT NULL AND D.histo_destruction <= SYSDATE THEN 'undelete' END import_action,
" . $this->formatColQuery($cols, ' CASE WHEN S.source_code IS NULL AND D.source_code IS NOT NULL THEN D.:column ELSE S.:column END :column', ",\n ") . ",
" . $this->formatColQuery($cols, ' CASE WHEN D.:column <> S.:column OR (D.:column IS NULL AND S.:column IS NOT NULL) OR (D.:column IS NOT NULL AND S.:column IS NULL) THEN 1 ELSE 0 END U_:column', ",\n ") . "
" . $this->formatColQuery($cols, function (Column $col) {
if ($col->dataType == 'CLOB') {
return ' CASE WHEN S.source_code IS NULL AND D.source_code IS NOT NULL THEN D.:column ELSE to_clob(S.:column) END :column';
} else {
return ' CASE WHEN S.source_code IS NULL AND D.source_code IS NOT NULL THEN D.:column ELSE S.:column END :column';
}
}, ",\n ") . ",
" . $this->formatColQuery($cols, function (Column $col) {
if ($col->dataType == 'CLOB') {
return ' CASE WHEN dbms_lob.compare(D.:column,S.:column) <> 0 OR (D.:column IS NULL AND S.:column IS NOT NULL) OR (D.:column IS NOT NULL AND S.:column IS NULL) THEN 1 ELSE 0 END U_:column';
} else {
return ' CASE WHEN D.:column <> S.:column OR (D.:column IS NULL AND S.:column IS NOT NULL) OR (D.:column IS NOT NULL AND S.:column IS NULL) THEN 1 ELSE 0 END U_:column';
}
}, ",\n ") . "
FROM
$tableName D$depJoin
FULL JOIN SRC_$tableName S ON S.source_id = D.source_id AND S.source_code = D.source_code$joinCond
......@@ -512,7 +531,13 @@ WHERE
(S.source_code IS NOT NULL AND D.source_code IS NOT NULL AND D.histo_destruction IS NOT NULL AND D.histo_destruction <= SYSDATE)
OR (S.source_code IS NULL AND D.source_code IS NOT NULL AND (D.histo_destruction IS NULL OR D.histo_destruction > SYSDATE))
OR (S.source_code IS NOT NULL AND D.source_code IS NULL)
OR " . $this->formatColQuery($cols, 'D.:column <> S.:column OR (D.:column IS NULL AND S.:column IS NOT NULL) OR (D.:column IS NOT NULL AND S.:column IS NULL)', "\n OR ") . "
OR " . $this->formatColQuery($cols, function (Column $col) {
if ($col->dataType == 'CLOB') {
return 'dbms_lob.compare(D.:column,S.:column) <> 0 OR (D.:column IS NULL AND S.:column IS NOT NULL) OR (D.:column IS NOT NULL AND S.:column IS NULL)';
} else {
return 'D.:column <> S.:column OR (D.:column IS NULL AND S.:column IS NOT NULL) OR (D.:column IS NOT NULL AND S.:column IS NULL)';
}
}, "\n OR ") . "
) diff JOIN source on source.id = diff.source_id WHERE import_action IS NOT NULL AND source.importable = 1";
return $sql;
......@@ -585,7 +610,7 @@ WHERE
END CASE;
EXCEPTION WHEN OTHERS THEN
".$this->getPackage().".SYNC_LOG( SQLERRM, '$tableName', diff_row.source_code );
" . $this->getPackage() . ".SYNC_LOG( SQLERRM, '$tableName', diff_row.source_code );
END;
END LOOP;
CLOSE diff_cur;
......@@ -613,7 +638,13 @@ WHERE
{
$res = [];
foreach ($cols as $col) {
$res[] = str_replace(':column', $col, $format);
if (is_callable($format)) {
$f = $format($col);
} else {
$f = $format;
}
$res[] = str_replace(':column', $col, $f);
}
return implode($separator, $res);
......
......@@ -54,6 +54,8 @@ class SchemaService extends AbstractService
$sc = [];
foreach( $d as $col ){
$column = new Column;
$column->table = $col['TABLE_NAME'];
$column->name = $col['COLUMN_NAME'];
$column->dataType = $col['DATA_TYPE'];
$column->length = (null === $col['LENGTH']) ? null : (integer)$col['LENGTH'];
$column->nullable = $col['NULLABLE'] == '1';
......@@ -102,7 +104,7 @@ class SchemaService extends AbstractService
}
/**
*
*
* @param string $tableName
* @param string $columnName
*/
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment