コード例 #1
0
ファイル: UpdaterBehaviorTest.php プロジェクト: keyeMyria/CRM
 /**
  * This test ensures that the updater will recover from failed migrations if
  * specified, and won't otherwise
  */
 public function testRecoveryFromFailedMigration()
 {
     // Prepare for the test
     $ube = $this->instantiateUBe();
     $this->prereq($ube, 'sourceDir');
     $nodes = array('protected', 'tests', 'data', 'updatemigration');
     $allNodes = '';
     foreach ($nodes as $node) {
         if (!is_dir($absNode = $ube->sourceDir . $allNodes . DIRECTORY_SEPARATOR . $node)) {
             mkdir($absNode);
         }
         $allNodes .= DIRECTORY_SEPARATOR . $node;
     }
     $migdir = implode(DIRECTORY_SEPARATOR, array_merge(array($ube->sourceDir), $nodes));
     // Copy the migration scripts
     $exceptionScript = 'protected/tests/data/updatemigration/failure-except.php';
     $errorScript = 'protected/tests/data/updatemigration/failure-error.php';
     copy($ube->webRoot . DIRECTORY_SEPARATOR . FileUtil::rpath($exceptionScript), $ube->sourceDir . DIRECTORY_SEPARATOR . FileUtil::rpath($exceptionScript));
     copy($ube->webRoot . DIRECTORY_SEPARATOR . FileUtil::rpath($errorScript), $ube->sourceDir . DIRECTORY_SEPARATOR . FileUtil::rpath($errorScript));
     // Ensure that backups can be restored when an exception is raised
     Yii::app()->db->createCommand("DROP TABLE IF EXISTS some_new_table;")->execute();
     $ube->makeDatabaseBackup();
     $scripts = array($exceptionScript);
     $ran = array();
     ob_start();
     try {
         $ube->runMigrationScripts($scripts, $ran, true);
         ob_end_clean();
     } catch (Exception $e) {
         ob_end_clean();
         $this->assertEquals(42000, $e->getCode(), 'Incorrect exception raised. This test ' . 'expects a PDO syntax error');
         $ube->restoreDatabaseBackup();
     }
     // Should have reverted
     $this->assertTableNotExists('some_new_table');
     // Ensure that backups can be restored when an error is raised
     $ran = array();
     $scripts = array($errorScript);
     ob_start();
     $ube->runMigrationScripts($scripts, $ran, true);
     // Retrieve error code from output
     $output = ob_get_clean();
     preg_match('/\\[(\\d+)\\]/', $output, $matches);
     $this->assertArrayHasKey(1, $matches);
     $this->assertEquals(8, $matches[1], 'Incorrect error occured. This test ' . 'expects an E_NOTICE: undefined variable.');
     // Should have reverted
     $this->assertTableNotExists('some_new_table');
     // Ensure that the database is in an 'unexpected' state when no recovery is performed
     $ran = array();
     $scripts = array($exceptionScript);
     ob_start();
     try {
         $ube->runMigrationScripts($scripts, $ran, false);
         ob_end_clean();
     } catch (Exception $e) {
         ob_end_clean();
         $this->assertEquals(42000, $e->getCode(), 'Incorrect exception raised. This test ' . 'expects a PDO syntax error');
     }
     $this->assertTableExists('some_new_table');
     Yii::app()->db->createCommand("DROP TABLE IF EXISTS some_new_table;")->execute();
 }
コード例 #2
0
 /**
  * Getter for {@link edition}
  *
  * @return string
  */
 public function getEdition()
 {
     if (!isset($this->_edition)) {
         if (YII_DEBUG) {
             switch (PRO_VERSION) {
                 case 1:
                     $this->_edition = 'pro';
                     break;
                 case 2:
                     $this->_edition = 'pla';
                     break;
                 default:
                     $this->_edition = 'opensource';
             }
         } else {
             $this->_edition = 'opensource';
             foreach (array('pla', 'pro') as $ed) {
                 $logo = "images/x2engine_crm_{$ed}.png";
                 $logoPath = implode(DIRECTORY_SEPARATOR, array($this->owner->basePath, '..', FileUtil::rpath($logo)));
                 if (file_exists($logoPath)) {
                     if (md5_file($logoPath) == self::$_logoHashes[$ed]) {
                         $this->_edition = $ed;
                         break;
                     }
                 }
             }
         }
     }
     return $this->_edition;
 }
コード例 #3
0
ファイル: FileOperTestCase.php プロジェクト: keyeMyria/CRM
 /**
  * Remove all the test files and directories.
  * @param bool $emptyFileList Whether to reset the properties that reference the file set
  */
 public function removeTestDirs($emptyFileList = true)
 {
     foreach (array_merge($this->files, $this->exclFiles) as $file) {
         $path = $this->baseDir . FileUtil::rpath("/{$file}");
         if (file_exists($path)) {
             unlink($path);
         }
     }
     foreach (array_merge($this->subDirs, $this->exclSubDirs) as $dir) {
         $path = $this->baseDir . FileUtil::rpath("/{$dir}");
         if (file_exists($path)) {
             rmdir($path);
         }
     }
     if (is_dir($this->baseDir)) {
         rmdir($this->baseDir);
     }
     if ($emptyFileList) {
         // Reset everything
         $this->resetTestDirs();
     }
 }
コード例 #4
0
ファイル: FileUtilTest.php プロジェクト: tymiles003/X2CRM
 public function testRelpath()
 {
     // Specifying both paths
     $startPoint = implode(DIRECTORY_SEPARATOR, array(Yii::app()->basePath, 'config', 'main.php'));
     $file = implode(DIRECTORY_SEPARATOR, array(Yii::app()->basePath, '..', 'framework', 'YiiBase.php'));
     $relpath = FileUtil::relpath($file, $startPoint);
     $this->assertEquals(str_replace('/', DIRECTORY_SEPARATOR, '../../framework/YiiBase.php'), $relpath);
     // Specifying only one path. The return value should originate from
     // index.php's directory!
     $relpath = FileUtil::relpath($file);
     $this->assertEquals('../../framework/YiiBase.php', $relpath);
     // Test on Windows!
     $startPoint = 'C:\\Program Files (x86)\\Something\\SomethingElse\\..\\something.exe';
     $endPoint = 'C:\\Windows\\Something\\..\\Something\\SomethingMore/library.dll';
     $relpath = FileUtil::relpath($endPoint, $startPoint, DIRECTORY_SEPARATOR);
     $this->assertEquals(FileUtil::rpath('../../Windows/Something/SomethingMore/library.dll'), $relpath);
     // Two ordinary points that don't require upward traversal...
     $startPoint = '/home/joeschmoe/public_html/';
     $endPoint = '/home/joeschmoe/public_html/protected/controllers/FatController.php';
     $relpath = FileUtil::relpath($endPoint, $startPoint);
     $this->assertEquals(FileUtil::rpath('protected/controllers/FatController.php'), $relpath);
     // Two points, one in a backup dir
     $startPoint = '/home/joeschmoe/public_html/protected/controllers/FatController.php';
     $endPoint = '/home/joeschmoe/public_html/backup/protected/controllers/FatController.php';
     $relpath = FileUtil::relpath($endPoint, $startPoint);
     $this->assertEquals(FileUtil::rpath('../../backup/protected/controllers/FatController.php'), $relpath);
 }
コード例 #5
0
 /**
  * Non-interactive fixture export with option to specify aliases as command line args
  */
 public function actionExport($tableName, $type = 'f', $range = 1, $columns = '*', $writeCond = 's', array $aliases = array())
 {
     $fileName = $tableName . ($type == 'i' ? '.init' : '') . '.php';
     $filePath = $this->fixtureDir . '/' . $tableName . ($type == 'i' ? '.init' : '') . '.php';
     if (file_exists(FileUtil::rpath($filePath))) {
         switch ($writeCond) {
             case 'r':
                 $i = 0;
                 $backup = $filePath;
                 while (file_exists(FileUtil::rpath($backup))) {
                     $backup = "{$filePath}.{$i}";
                     $i++;
                 }
                 $this->copyFiles(array("backup of existing: {$fileName}" => array('source' => $filePath, 'target' => $backup)));
                 break;
             case 'o':
                 echo "\nOverwriting existing file {$fileName}\n";
                 break;
             case 's':
                 break;
             default:
                 // filename
                 echo "\nWriting to file {$writeCond}\n";
         }
     }
     $aliasPrompt = false;
     if ($type == 'f' && $this->_mode === 'interactive') {
         $aliasPrompt = $this->confirm('Prompt for row aliases?');
     }
     $records = Yii::app()->db->createCommand()->select($columns)->from($tableName)->where($range)->queryAll();
     $fileCont = "<?php\nreturn array(\n";
     foreach ($records as $index => $record) {
         $alias = null;
         if ($type == 'f') {
             if (!$aliasPrompt && isset($aliases[$index])) {
                 $alias = $aliases[$index];
             } else {
                 $alias = $index;
             }
             if ($aliasPrompt) {
                 var_dump($record);
                 $alias = $this->prompt("Alias for this record (enter for \"{$index}\"):");
                 if (empty($alias)) {
                     $alias = $index;
                 }
                 while (in_array($alias, $aliases)) {
                     $alias = $this->prompt("Alias in use already. Enter another:");
                     if (empty($alias)) {
                         $alias = $index;
                     }
                 }
                 $aliases[] = $alias;
             } else {
             }
         }
         $fileCont .= $this->formatRecord($record, $alias);
     }
     $fileCont .= ");\n?>";
     if (!in_array($writeCond, array('s', 'r', 'o'))) {
         file_put_contents($writeCond, $fileCont);
     } elseif ($writeCond !== 's') {
         file_put_contents($filePath, $fileCont);
     } else {
         /**/
         print $fileCont;
     }
     echo "\nExport complete.\n";
 }
コード例 #6
0
ファイル: UpdaterBehavior.php プロジェクト: keyeMyria/CRM
 /**
  * In which the updater downloads a new version of itself.
  * 
  * @param type $updaterCheck New version of the update utility
  * @return array
  */
 public function updateUpdater($updaterCheck)
 {
     if (version_compare($this->configVars['updaterVersion'], $updaterCheck) >= 0) {
         return array();
     }
     $updaterFiles = $this->updaterFiles;
     // Retrieve the update package contents' files' digests:
     $md5sums_content = FileUtil::getContents($this->updateServer . '/' . $this->getUpdateDataRoute($this->configVars['updaterVersion']) . '/contents.md5');
     // If there's an error on the server end the response will be a JSON
     $tryJson = json_decode($md5sums_content, 1);
     if (!(bool) $md5sums_content) {
         $admin = CActiveRecord::model('Admin')->findByPk(1);
         if ($this->scenario === 'upgrade' && isset($admin) && empty($admin->unique_key)) {
             $updaterSettingsLink = CHtml::link(Yii::t('admin', 'Updater Settings page'), array('admin/updaterSettings'));
             throw new CException(Yii::t('admin', 'You must first set a product key on the ' . $updaterSettingsLink));
         } else {
             throw new CException(Yii::t('admin', 'Unknown update server error.'), self::ERR_UPSERVER);
         }
     } else {
         if (is_array($tryJson)) {
             // License key error
             if (isset($tryJson['errors'])) {
                 throw new CException($tryJson['errors']);
             } else {
                 throw new CException(Yii::t('admin', 'Unknown update server error.') . ' ' . $md5sums_content);
             }
         }
     }
     preg_match_all(':^(?<md5sum>[a-f0-9]{32})\\s+source/protected/(?<filename>\\S.*)$:m', $md5sums_content, $md5s);
     $md5sums = array();
     for ($i = 0; $i < count($md5s[0]); $i++) {
         $md5sums[$md5s['md5sum'][$i]] = $md5s['filename'][$i];
     }
     // These are the files that need to be downloaded -- only those which have changed:
     $updaterFiles = array_intersect($md5sums, $updaterFiles);
     // Try to retrieve the files:
     $failed2Retrieve = array();
     foreach ($updaterFiles as $md5 => $file) {
         $pass = 0;
         $tries = 0;
         $downloadedFile = FileUtil::relpath(implode(DIRECTORY_SEPARATOR, array($this->webRoot, self::TMP_DIR, 'protected', FileUtil::rpath($file))), $this->thisPath . DIRECTORY_SEPARATOR);
         while (!$pass && $tries < 2) {
             $remoteFile = $this->updateServer . '/' . $this->sourceFileRoute . "/protected/{$file}";
             try {
                 $this->downloadSourceFile("protected/{$file}");
             } catch (Exception $e) {
                 break;
             }
             // Only call it done if it's intact and ready for use:
             $pass = md5_file($downloadedFile) == $md5;
             $tries++;
         }
         if (!$pass) {
             $failed2Retrieve[] = "protected/{$file}";
         }
     }
     $failedDownload = (bool) count($failed2Retrieve);
     // Copy the files into the live install
     if (!$failedDownload && (bool) count($updaterFiles)) {
         $this->applyFiles(self::TMP_DIR);
         // Remove the temporary directory:
         FileUtil::rrmdir($this->webRoot . DIRECTORY_SEPARATOR . self::TMP_DIR);
     } else {
         $errorResponse = json_decode($md5sums_content, 1);
         if (isset($errorResponse['errors'])) {
             throw new CException($errorResponse['errors']);
         }
     }
     // Write the new updater version into the configuration; else
     // the app will get stuck in a redirect loop
     if (!$failedDownload) {
         $this->regenerateConfig(Null, $updaterCheck, Null);
     }
     return $failed2Retrieve;
 }
コード例 #7
0
 /**
  * Export the contents of a table in the live database as a fixture or init script.
  * 
  * Usage:
  * <tt>./yiic exportfixture [table name] [f|i] [range] [columns] [o|r]</tt>
  * 
  * @param array $args 
  */
 public function run($args)
 {
     $this->fixtureDir = Yii::app()->basePath . '/tests/fixtures';
     foreach ($this->args as $pos => $spec) {
         $valid = false;
         while (!$valid) {
             if (array_key_exists($pos, $args)) {
                 ${$spec[0]} = $args[$pos];
                 $valid = $this->validInput($args[$pos], $spec[3]);
                 if (!$valid) {
                     echo $this->errorMessage($spec, ${$spec[4]});
                     echo $this->getHelp();
                     Yii::app()->end();
                 }
             } else {
                 ${$spec[0]} = $this->prompt("{$spec[0]} ({$spec[1]})", $spec[2]);
                 $valid = $this->validInput(${$spec[0]}, $spec[3]);
                 if (!$valid) {
                     echo $this->errorMessage($spec, ${$spec[0]});
                 }
             }
         }
     }
     if (!$valid) {
         echo $this->getHelp();
         Yii::app()->end();
     }
     $fileName = $tableName . ($type == 'i' ? '.init' : '') . '.php';
     $filePath = $this->fixtureDir . '/' . $tableName . ($type == 'i' ? '.init' : '') . '.php';
     if (file_exists(FileUtil::rpath($filePath))) {
         switch ($writeCond) {
             case 'r':
                 $i = 0;
                 $backup = $filePath;
                 while (file_exists(FileUtil::rpath($backup))) {
                     $backup = "{$filePath}.{$i}";
                     $i++;
                 }
                 $this->copyFiles(array("backup of existing: {$fileName}" => array('source' => $filePath, 'target' => $backup)));
                 break;
             case 'o':
                 echo "\nOverwriting existing file {$fileName}\n";
                 break;
             case 's':
                 break;
             default:
                 // filename
                 echo "\nWriting to file {$writeCond}\n";
         }
     }
     $aliasPrompt = false;
     if ($type == 'f') {
         $aliasPrompt = $this->confirm('Prompt for row aliases?');
     }
     $records = Yii::app()->db->createCommand()->select($columns)->from($tableName)->where($range)->queryAll();
     $fileCont = "<?php\nreturn array(\n";
     $aliases = array();
     foreach ($records as $index => $record) {
         $alias = null;
         if ($type == 'f') {
             $alias = $index;
             if ($aliasPrompt) {
                 var_dump($record);
                 $alias = $this->prompt("Alias for this record (enter for \"{$index}\"):");
                 if (empty($alias)) {
                     $alias = $index;
                 }
                 while (in_array($alias, $aliases)) {
                     $alias = $this->prompt("Alias in use already. Enter another:");
                     if (empty($alias)) {
                         $alias = $index;
                     }
                 }
                 $aliases[] = $alias;
             } else {
             }
         }
         $fileCont .= $this->formatRecord($record, $alias);
     }
     $fileCont .= ");\n?>";
     if (!in_array($writeCond, array('s', 'r', 'o'))) {
         file_put_contents($writeCond, $fileCont);
     } elseif ($writeCond !== 's') {
         file_put_contents($filePath, $fileCont);
     } else {
         /**/
         print $fileCont;
     }
     echo "\nExport complete.\n";
 }
コード例 #8
0
ファイル: SampleDataCommand.php プロジェクト: keyeMyria/CRM
 /**
  * Exports the database content into dummy data files
  * 
  * @param array $args
  * @param PDOException $e
  * @return type 
  */
 public function actionExport($args)
 {
     if (!copy("./data/install_timestamp", "./data/dummy_data_date")) {
         die("Error: actionExport: failed to copy install_timestamp to dummy_data_date");
     }
     // [edition] => [array of table names]
     $tblEditions = (require realpath(Yii::app()->basePath . '/data/nonFreeTables.php'));
     $allEditions = array_keys($tblEditions);
     $nonFreeEditions = array_diff($allEditions, array('opensource'));
     $specTemplate = array_fill_keys($allEditions, array());
     $this->pdo = Yii::app()->db->pdoInstance;
     $conf = realpath(Yii::app()->basePath . '/config/X2Config.php');
     if ($conf) {
         if ((include $conf) !== 1) {
             die('Configuration import failed.');
         }
     } else {
         die("Configuration file not found. This script must be run in protected/data.\n");
     }
     $getTbls = $this->pdo->prepare("SHOW TABLES IN `{$dbname}`");
     $getTbls->execute();
     try {
         $allTbls = array_map(function ($tr) use($dbname) {
             return $tr["Tables_in_{$dbname}"];
         }, $getTbls->fetchAll(PDO::FETCH_ASSOC));
     } catch (PDOException $e) {
         die("Database error: " . $e->getMessage() . "\n");
     }
     /**
      * The command for exporting data:
      */
     $command = "mysqldump -tc -u {$user} -p{$pass} {$dbname} ";
     // Ignore pattern for lines in output of mysqldump:
     $lPat = '/^(\\/\\*|\\-\\-|\\s*$';
     // Export current app's data as "dummy" (usage example) data
     $lPat .= '|(?:UN)?LOCK TABLES)/';
     $out = FileUtil::rpath(Yii::app()->basePath . '/data/dummy_data%s.sql');
     /**
      * Update the list of tables for each edition with the default tables:
      */
     $nonFreeTbls = array_reduce($allEditions, function ($a, $e) use($tblEditions) {
         return array_merge($tblEditions[$e], $a);
     }, array());
     $tblEditions['opensource'] = array_diff($allTbls, $nonFreeTbls);
     /**
      * Declare the export specification arrays
      *
      * Here it's specified what data will be exported and how.
      * Each of these arrays follows the basic pattern of $specTemplate:
      * [edition] => [array of table names or ([table name] =>[spec])]
      */
     /**
      * These will be excluded from data export altogether
      */
     $tblsExclude = $specTemplate;
     // These will be excluded for open source and above:
     $tblsExclude['opensource'] = array_merge(array('x2_admin', 'x2_auth_assignment', 'x2_auth_item', 'x2_auth_item_child', 'x2_modules', 'x2_sessions', 'x2_temp_files', 'x2_timezones', 'x2_timezone_points', 'x2_tips'), $tblEditions['pro'], $tblEditions['pla']);
     // These for professional edition:
     $tblsExclude['pro'] = array_merge(array('x2_forwarded_email_patterns'), $tblEditions['pla']);
     // These for platform/platinum edition:
     $tblsExclude['pla'] = array('x2_forwarded_email_patterns');
     /**
      * These will be included, but with specific criteria
      */
     $tblsWhere = $specTemplate;
     $tblsWhere['opensource'] = array('x2_dropdowns' => 'id>=1000', 'x2_fields' => 'custom=1', 'x2_form_layouts' => 'id>=1000', 'x2_media' => '(id>11 AND id<1000) OR (id>1006 AND id<2000) OR id>2002', 'x2_profile' => 'id>2', 'x2_users' => 'id>2', 'x2_social' => 'id>1', 'x2_docs' => 'id>52 OR id<52');
     /**
      * Update statements will be generated for these tables on which there's no way
      * of inserting it at install time without running into duplicate primary key
      * errors (because it's a record inserted by the installer itself). In each table:
      * 'pk' =>  primary key (string for single-column or array for multi-column)
      * 'fields' => array of fields to update or "*" to update all fields. Must include primary key.
      * 'where' => records for which to generate update statements
      */
     $tblsChangeDefault = $specTemplate;
     $tblsChangeDefault['opensource'] = array('x2_profile' => array('pk' => 'id', 'fields' => '*', 'where' => '`id`=1'), 'x2_users' => array('pk' => 'id', 'fields' => array('id', 'firstName', 'lastName', 'officePhone', 'cellPhone', 'showCalendars', 'calendarViewPermission', 'calendarEditPermission', 'calendarFilter', 'setCalendarPermissions', 'recentItems', 'topContacts'), 'where' => '`id`=1'));
     /**
      * Switch the order of output generation so that foreign key constraints don't 
      * fail during insertion. List dependencies here.
      */
     $insertFirst = $specTemplate;
     $insertFirst['opensource'] = array('x2_list_criteria' => array('x2_lists'), 'x2_list_items' => array('x2_lists'), 'x2_role_to_workflow' => array('x2_workflow_stages', 'x2_roles', 'x2_workflows'), 'x2_workflow_stages' => array('x2_workflows'), 'x2_action_text' => array('x2_actions'));
     /**
      * This array stores tables to be executed "next"
      */
     $insertNext = $specTemplate;
     /**
      * The resulting SQL to be written to files 
      */
     $allSql = $specTemplate;
     /**
      * Assemble the array of combined export specs.
      * 
      * Note that since the "where" conditions are put in the array last, they'll
      * take precedence (so if it's listed in both $tblsExclude and $tblsWhere, 
      * only $tblsWhere will apply).
      */
     $allTbls = array();
     foreach ($allEditions as $edition) {
         $allTbls[$edition] = array_fill_keys($tblEditions[$edition], true);
         foreach ($tblsExclude[$edition] as $tbl) {
             $allTbls[$edition][$tbl] = false;
         }
         foreach ($tblsWhere[$edition] as $tbl => $where) {
             $allTbls[$edition][$tbl] = $where;
         }
     }
     // The update statement that will be used for updating records post-insertion:
     $updateStatement = "UPDATE `%s` SET %s WHERE %s;";
     foreach ($nonFreeEditions as $edition) {
         $allSql[$edition][] = "/* @edition:{$edition} */";
     }
     /**
      * Generate SQL for the data:
      */
     foreach ($allTbls as $edition => $tbls) {
         /**
          * Generate insertion statements 
          */
         $eTbls = $tbls;
         while (count($eTbls) > 0) {
             $tblsTmp = $eTbls;
             foreach ($tblsTmp as $tbl => $where) {
                 if ($where != false) {
                     // This table is to be included in the data export
                     if (array_key_exists($tbl, $insertFirst[$edition])) {
                         // This table depends on other tables being ready with data
                         $skip = False;
                         foreach ($insertFirst[$edition][$tbl] as $tblFirst) {
                             // Check to see if the table has been accounted for already
                             if (array_key_exists($tblFirst, $eTbls)) {
                                 $skip = True;
                                 break;
                             }
                         }
                         if ($skip) {
                             // Not all dependencies of this table have been resolved yet.
                             continue;
                         }
                     }
                     $output = array();
                     $tblCommand = "{$command} {$tbl}" . ($where !== true ? " --where='" . $where . "' " : ' ');
                     exec($tblCommand, $output);
                     foreach ($output as $line) {
                         if (!preg_match($lPat, $line)) {
                             $allSql[$edition][] = $line;
                         }
                     }
                 }
                 unset($eTbls[$tbl]);
             }
         }
         /**
          * Generate update statements 
          */
         foreach ($tblsChangeDefault[$edition] as $tbl => $how) {
             $colSel = $how['fields'];
             if (is_array($how['fields'])) {
                 $colSel = '`' . implode('`,`', $how['fields']) . '`';
             }
             $query = $this->pdo->prepare("SELECT {$colSel} FROM `{$tbl}` WHERE {$how['where']}");
             $query->execute();
             $recs = $query->fetchAll(PDO::FETCH_ASSOC);
             $pk = $how['pk'];
             if (!is_array($pk)) {
                 $pk = array($pk);
             }
             foreach ($recs as $rec) {
                 // Generate a "where" clause criterion to refer to this record by its primary key
                 $whereSelector = array();
                 foreach ($pk as $c) {
                     $whereSelector[] = "`{$c}`=" . $this->sqlValue($rec[$c]);
                 }
                 // Exclude the primary key from the columns to be updated:
                 foreach ($pk as $col) {
                     unset($rec[$col]);
                 }
                 $fieldsSet = array();
                 foreach ($rec as $col => $val) {
                     $fieldsSet[] = "`{$col}`=" . $this->sqlValue($val);
                 }
                 $allSql[$edition][] = sprintf($updateStatement, $tbl, implode(',', $fieldsSet), implode(' AND ', $whereSelector));
             }
         }
     }
     // Create dummy data files
     foreach ($allSql as $edition => $sqls) {
         file_put_contents(sprintf($out, $edition == 'opensource' ? '' : "-{$edition}"), implode("\n/*&*/\n", $sqls));
     }
 }