154 lines
5.5 KiB
PHP
154 lines
5.5 KiB
PHP
<?php
|
|
|
|
namespace App\Console\Commands;
|
|
|
|
use Illuminate\Console\Command;
|
|
use Illuminate\Support\Facades\DB;
|
|
use Illuminate\Support\Facades\File;
|
|
|
|
class RetryCopyCommand extends Command
|
|
{
|
|
protected $signature = 'db:re-copy {table : The name of the table to retry the native COPY command for.}';
|
|
protected $description = 'Finds the latest CSV file and attempts to execute the native PostgreSQL COPY FROM (server-side) using DB::statement().';
|
|
|
|
// Base directory, MUST BE ACCESSIBLE by the PostgreSQL service user.
|
|
protected $baseTempDir = 'C:\Temp\pg_imports';
|
|
|
|
public function handle()
|
|
{
|
|
$tableName = $this->argument('table');
|
|
$this->info("===============================================================");
|
|
$this->info("🔄 ATTEMPTING NATIVE DB::statement() COPY for **{$tableName}**");
|
|
$this->info("===============================================================");
|
|
|
|
if (!File::isDirectory($this->baseTempDir)) {
|
|
$this->error("❌ ERROR: Base directory not found: {$this->baseTempDir}");
|
|
return Command::FAILURE;
|
|
}
|
|
|
|
// 1. Find the latest CSV file
|
|
$latestFile = $this->findLatestCsvFile($tableName);
|
|
|
|
if (!$latestFile) {
|
|
$this->error("❌ ERROR: No recent CSV file found for table '{$tableName}' in {$this->baseTempDir}.");
|
|
return Command::FAILURE;
|
|
}
|
|
|
|
$fullPath = $this->baseTempDir . DIRECTORY_SEPARATOR . $latestFile;
|
|
// Important: PostgreSQL requires forward slashes or escaped backslashes in the path string
|
|
$pgPath = str_replace('\\', '/', $fullPath);
|
|
$this->line("✅ Using file path: **{$pgPath}**");
|
|
|
|
// 2. Prepare database environment
|
|
$this->disablePostgresConstraints();
|
|
$this->warn('Constraints temporarily disabled.');
|
|
|
|
// Obtener las columnas preservando el case original
|
|
$columns = $this->getTableColumns($tableName);
|
|
$columnList = implode(', ', array_map(function($c) { return "\"$c\""; }, $columns));
|
|
|
|
try {
|
|
// 3. Execute COPY FROM using DB::statement()
|
|
// Usar comillas dobles para preservar el case del nombre de la tabla
|
|
$sql = "COPY \"{$tableName}\" ({$columnList}) FROM '$pgPath' WITH (DELIMITER E'\t', FORMAT CSV, ENCODING 'UTF-8', QUOTE '\"', ESCAPE '\\')";
|
|
|
|
$this->line("Executing SQL: COPY \"{$tableName}\" FROM '{$pgPath}'...");
|
|
DB::statement($sql);
|
|
|
|
// 4. Finalization
|
|
$this->enablePostgresConstraints();
|
|
$this->resetPostgresSequences($tableName);
|
|
|
|
// Re-check the file exists before deleting, just in case
|
|
if (file_exists($fullPath)) {
|
|
unlink($fullPath);
|
|
$this->warn("Temporary file deleted: {$latestFile}");
|
|
}
|
|
|
|
$this->info("🎉 SUCCESS! Data copied successfully using DB::statement().");
|
|
return Command::SUCCESS;
|
|
|
|
} catch (\Exception $e) {
|
|
$this->error("❌ COPY failed via DB::statement(). Error: " . $e->getMessage());
|
|
$this->error("HINT: If the error is 'Permission denied', the service user cannot read the file at {$pgPath}.");
|
|
$this->enablePostgresConstraints();
|
|
return Command::FAILURE;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Obtiene las columnas de la tabla preservando el case original
|
|
*/
|
|
protected function getTableColumns(string $tableName): array
|
|
{
|
|
$query = "
|
|
SELECT column_name
|
|
FROM information_schema.columns
|
|
WHERE table_name = ?
|
|
ORDER BY ordinal_position
|
|
";
|
|
|
|
$columns = DB::select($query, [$tableName]);
|
|
|
|
return array_map(function($col) {
|
|
return $col->column_name;
|
|
}, $columns);
|
|
}
|
|
|
|
/**
|
|
* Finds the most recent CSV file matching the table name pattern.
|
|
*/
|
|
protected function findLatestCsvFile(string $tableName): ?string
|
|
{
|
|
$files = File::files($this->baseTempDir);
|
|
$latestFile = null;
|
|
$latestTime = 0;
|
|
// Usar preg_quote para escapar caracteres especiales en el nombre de la tabla
|
|
$escapedTableName = preg_quote($tableName, '/');
|
|
$pattern = "/^{$escapedTableName}_import_temp_\d+\.csv$/";
|
|
|
|
foreach ($files as $file) {
|
|
$fileName = $file->getFilename();
|
|
if (preg_match($pattern, $fileName)) {
|
|
$fileTime = $file->getMTime();
|
|
if ($fileTime > $latestTime) {
|
|
$latestTime = $fileTime;
|
|
$latestFile = $fileName;
|
|
}
|
|
}
|
|
}
|
|
|
|
return $latestFile;
|
|
}
|
|
|
|
// Auxiliary Methods
|
|
protected function getPkColumn(string $tableName): string
|
|
{
|
|
// Placeholder: Needs implementation using getSerialColumns or schema inspection
|
|
return 'id' . $tableName;
|
|
}
|
|
|
|
protected function disablePostgresConstraints(): void
|
|
{
|
|
DB::statement("SET session_replication_role = 'replica'");
|
|
}
|
|
|
|
protected function enablePostgresConstraints(): void
|
|
{
|
|
DB::statement("SET session_replication_role = 'origin'");
|
|
}
|
|
|
|
protected function resetPostgresSequences(string $tableName): void
|
|
{
|
|
// This method requires the full implementation from the main command.
|
|
// For brevity, we assume it's copied over correctly.
|
|
}
|
|
|
|
protected function getSerialColumns(string $tableName): array
|
|
{
|
|
// This method requires the full implementation from the main command.
|
|
// For brevity, we assume it's copied over correctly.
|
|
return [];
|
|
}
|
|
}
|