FOSSology  4.4.0
Open Source License Compliance by Open Source Software
dbmigrate_3.7-3.8.php
Go to the documentation of this file.
1 <?php
2 /*
3  SPDX-FileCopyrightText: © 2020 Siemens AG
4  Author: Gaurav Mishra <mishra.gaurav@siemens.com>
5 
6  SPDX-License-Identifier: GPL-2.0-only
7 */
8 
15 
20 const MAX_ROW_SIZE = 100;
21 
26 const ENCODE_TABLES = array(
27  "copyright",
28  "author",
29  "ecc",
30  "keyword"
31 );
32 
38 {
39  $selectSql = "SELECT count(*) AS cnt FROM ";
40  $where = " WHERE (content IS NOT NULL AND content != '')";
41  $statement = __METHOD__ . ".getCountsFor";
42  $count = 0;
43  foreach (ENCODE_TABLES as $table) {
44  $sql = $selectSql . $table . $where;
45  $row = $dbManager->getSingleRow($sql, array(), $statement . $table);
46  $count += intval($row['cnt']);
47  }
48  return $count;
49 }
50 
58 function updateRecodedValues($dbManager, $table, $rows)
59 {
60  if (empty($rows)) {
61  return 0;
62  }
63 
64  $values = array_map(function($id,$content)
65  {
66  return "($id,'" . pg_escape_string($content) . "')";
67  }, array_keys($rows), $rows);
68 
69  $sql = "UPDATE $table AS cp " .
70  "SET content = up.content, hash = md5(up.content) " .
71  "FROM (VALUES" . join(",", $values) .
72  ") AS up(id,content) " .
73  "WHERE up.id = cp." . $table . "_pk";
74  unset($values);
75  $statement = __METHOD__ . ".updateContentFor.$table";
76 
77  $dbManager->begin();
78  $dbManager->queryOnce($sql, $statement);
79  $dbManager->commit();
80  return count($rows);
81 }
82 
90 function recodeContents(&$rows, $MODDIR)
91 {
92  $cmd = "$MODDIR/copyright/agent/fo_unicode_clean";
93  $descriptorspec = array(
94  0 => array("pipe", "r"),
95  1 => array("pipe", "w")
96  );
97 
98  $input = "";
99  foreach ($rows as $key => $content) {
100  if (empty($content)) {
101  continue;
102  }
103  $input .= "$key,|>$content<|\n";
104  }
105 
106  $pipes = array();
107  $escaper = proc_open($cmd, $descriptorspec, $pipes);
108 
109  if (is_resource($escaper)) {
110  fwrite($pipes[0], $input);
111  fclose($pipes[0]);
112  unset($input);
113 
114  $output = trim(stream_get_contents($pipes[1]));
115  fclose($pipes[1]);
116 
117  $returnVal = proc_close($escaper);
118  assert($returnVal == 0, "Encoder tool failed. Returned $returnVal.");
119  } else {
120  throw new \UnexpectedValueException("Unable to fork encoder tool ($cmd)");
121  }
122 
123  $output = explode("<|\n", $output);
124 
125  // Remove last delimiter as well
126  $lastIndex = count($output) - 1;
127  $output[$lastIndex] = str_replace("<|", "", $output[$lastIndex]);
128 
129  foreach ($output as $row) {
130  if (empty($row)) {
131  continue;
132  }
133  $line = explode(",|>", $row);
134  $id = $line[0];
135  $recodedContent = $line[1];
136  if ($rows[$id] == $recodedContent) {
137  unset($rows[$id]);
138  } else {
139  $rows[$id] = $recodedContent;
140  }
141  }
142 }
143 
148 function startRecodingTables($dbManager, $MODDIR)
149 {
150  if ($dbManager == NULL) {
151  echo "No connection object passed!\n";
152  return false;
153  }
154 
155  $sql = "SHOW client_encoding;";
156  $statement = __METHOD__ . ".getOldClientEncoding";
157  $oldEnc = $dbManager->getSingleRow($sql, array(), $statement);
158  $oldEnc = $oldEnc['client_encoding'];
159 
160  $sql = "SET client_encoding = 'SQL_ASCII';";
161  $dbManager->queryOnce($sql);
162  $where = " WHERE (content IS NOT NULL AND content != '')";
163 
164  foreach (ENCODE_TABLES as $table) {
165  $countSql = "SELECT count(*) AS cnt FROM $table $where;";
166  $countStatement = __METHOD__ . ".getCountFor.$table";
167  $contentSql = "SELECT " . $table . "_pk AS id, content " .
168  "FROM $table $where " .
169  "ORDER BY " . $table . "_pk " .
170  "LIMIT $1 OFFSET $2;";
171  $contentStatement = __METHOD__ . ".getContentFor.$table";
172 
173  $length = $dbManager->getSingleRow($countSql, array(), $countStatement);
174  $length = $length['cnt'];
175  echo "*** Recoding $length records from $table table ***\n";
176  $i = 0;
177  $updatedCount = 0;
178  while ($i < $length) {
179  $rows = $dbManager->getRows($contentSql, array(MAX_ROW_SIZE, $i),
180  $contentStatement);
181  $i += count($rows);
182  $data = array();
183  foreach ($rows as $row) {
184  if (empty($row['content'])) {
185  continue;
186  }
187  $data[$row['id']] = $row['content'];
188  }
189  recodeContents($data, $MODDIR);
190  $updatedCount += updateRecodedValues($dbManager, $table, $data);
191  if ($i % 10000 == 0) {
192  echo " Processed $i rows for $table table\n";
193  }
194  }
195  echo "*** Recoded $updatedCount for $table table ***\n";
196  }
197 
198  $sql = "SET client_encoding = '$oldEnc';";
199  $dbManager->queryOnce($sql);
200 }
201 
207 function getDbEncoding($dbManager)
208 {
209  $dbName = $GLOBALS["SysConf"]["DBCONF"]["dbname"];
210  $sql = "SELECT pg_encoding_to_char(encoding) AS encoding " .
211  "FROM pg_database WHERE datname = '$dbName';";
212  $row = $dbManager->getSingleRow($sql);
213  return $row["encoding"];
214 }
215 
220 function updateDbEncoding($dbManager)
221 {
222  if (strcasecmp(getDbEncoding($dbManager), 'sql_ascii') == 0) {
223  $dbName = $GLOBALS["SysConf"]["DBCONF"]["dbname"];
224  $sql = "UPDATE pg_database SET encoding = pg_char_to_encoding('UTF8') " .
225  "WHERE datname = '$dbName';";
226  $cmd = 'su postgres --command "psql --command=\"BEGIN;' . $sql .
227  '\"COMMIT;"';
228  shell_exec($cmd);
229  }
230 }
231 
237 function checkMigrate3738Required($dbManager)
238 {
239  if ($dbManager == NULL){
240  echo "No connection object passed!\n";
241  return false;
242  }
243 
244  $migRequired = true;
245  foreach (ENCODE_TABLES as $table) {
246  if (DB_TableExists($table) != 1) {
247  $migRequired = false;
248  break;
249  }
250  }
251  if ($migRequired) {
252  $migRequired = false;
253  if (strcasecmp(getDbEncoding($dbManager), 'sql_ascii') == 0) {
254  $migRequired = true;
255  }
256  }
257 
258  return $migRequired;
259 }
260 
268 function recodeTables($dbManager, $MODDIR, $force = false)
269 {
270  if (! checkMigrate3738Required($dbManager)) {
271  // Migration not required
272  // Still change encoding of database from SQL_ASCII to UTF-8
273  updateDbEncoding($dbManager);
274  return 0;
275  }
276  $totalRecords = calculateNumberOfRecordsToRecode($dbManager);
277 
278  if ($totalRecords == 0) {
279  // Migration not required
280  return 0;
281  }
282  $envYes = getenv('FOSSENCODING');
283  if (!$force) {
284  $force = !empty($envYes);
285  }
286 
287  echo "*** Recoding $totalRecords records to UTF-8. ***\n";
288 
289  if (!$force && $totalRecords > 500000) {
290  $REDCOLOR = "\033[0;31m";
291  $NOCOLOR = "\033[0m";
292  echo "\n*********************************************************" .
293  "***********************\n";
294  echo "*** " . $REDCOLOR . "Error, script will take too much time. Not " .
295  "recoding entries. " . $NOCOLOR . " ***\n";
296  echo "*** Either rerun the fo-postinstall with \"--force-encode\" flag " .
297  "or set ***\n" .
298  "*** \"FOSSENCODING=1\" in environment or run script at " .
299  " ***\n";
300  echo "*** \"" . dirname(__FILE__) .
301  "/dbmigrate_change_db_encoding.php\" to continue as a separate process ***\n";
302  echo "*********************************************************" .
303  "***********************\n";
304  return -25;
305  }
306 
307  try {
308  echo "*** Recoding entries in copyright and sister tables ***\n";
309  startRecodingTables($dbManager, $MODDIR);
310  updateDbEncoding($dbManager);
311  } catch (Exception $e) {
312  echo "*** Something went wrong. Try again! ***\n";
313  $dbManager->rollback();
314  return -1;
315  }
316 }
317 
322 function Migrate_37_38($dbManager, $MODDIR)
323 {
324  if (! checkMigrate3738Required($dbManager)) {
325  // Migration not required
326  // Still change encoding of database from SQL_ASCII to UTF-8
327  updateDbEncoding($dbManager);
328  return;
329  }
330  try {
331  recodeTables($dbManager, $MODDIR);
332  } catch (Exception $e) {
333  echo "Something went wrong. Try running postinstall again!\n";
334  $dbManager->rollback();
335  }
336 }
DB_TableExists($tableName)
Check if table exists.
Definition: common-db.php:214
getDbEncoding($dbManager)
updateDbEncoding($dbManager)
recodeContents(&$rows, $MODDIR)
updateRecodedValues($dbManager, $table, $rows)
startRecodingTables($dbManager, $MODDIR)
Migrate_37_38($dbManager, $MODDIR)
const MAX_ROW_SIZE
checkMigrate3738Required($dbManager)
calculateNumberOfRecordsToRecode($dbManager)
recodeTables($dbManager, $MODDIR, $force=false)
const ENCODE_TABLES
char * trim(char *ptext)
Trimming whitespace.
Definition: fossconfig.c:690