function commit_symbols($syms, $project, $no_purge)
{
    echo "Looking up path IDs...\n";
    $path_map = PhabricatorRepositoryCommitChangeParserWorker::lookupOrCreatePaths(ipull($syms, 'path'));
    $symbol = new PhabricatorRepositorySymbol();
    $conn_w = $symbol->establishConnection('w');
    echo "Preparing queries...\n";
    $sql = array();
    foreach ($syms as $dict) {
        $sql[] = qsprintf($conn_w, '(%d, %s, %s, %s, %s, %d, %d)', $project->getID(), $dict['ctxt'], $dict['name'], $dict['type'], $dict['lang'], $dict['line'], $path_map[$dict['path']]);
    }
    if (!$no_purge) {
        echo "Purging old syms...\n";
        queryfx($conn_w, 'DELETE FROM %T WHERE arcanistProjectID = %d', $symbol->getTableName(), $project->getID());
    }
    echo "Loading " . number_format(count($sql)) . " syms...\n";
    foreach (array_chunk($sql, 128) as $chunk) {
        queryfx($conn_w, 'INSERT INTO %T
      (arcanistProjectID, symbolContext, symbolName, symbolType,
        symbolLanguage, lineNumber, pathID) VALUES %Q', $symbol->getTableName(), implode(', ', $chunk));
    }
}
function commit_symbols(array $symbols, PhabricatorRepository $repository, $no_purge)
{
    echo pht('Looking up path IDs...'), "\n";
    $path_map = PhabricatorRepositoryCommitChangeParserWorker::lookupOrCreatePaths(ipull($symbols, 'path'));
    $symbol = new PhabricatorRepositorySymbol();
    $conn_w = $symbol->establishConnection('w');
    echo pht('Preparing queries...'), "\n";
    $sql = array();
    foreach ($symbols as $dict) {
        $sql[] = qsprintf($conn_w, '(%s, %s, %s, %s, %s, %d, %d)', $repository->getPHID(), $dict['ctxt'], $dict['name'], $dict['type'], $dict['lang'], $dict['line'], $path_map[$dict['path']]);
    }
    if (!$no_purge) {
        echo pht('Purging old symbols...'), "\n";
        queryfx($conn_w, 'DELETE FROM %T WHERE repositoryPHID = %s', $symbol->getTableName(), $repository->getPHID());
    }
    echo pht('Loading %s symbols...', phutil_count($sql)), "\n";
    foreach (array_chunk($sql, 128) as $chunk) {
        queryfx($conn_w, 'INSERT INTO %T
        (repositoryPHID, symbolContext, symbolName, symbolType,
        symbolLanguage, lineNumber, pathID) VALUES %Q', $symbol->getTableName(), implode(', ', $chunk));
    }
}
 /**
  * Update the table which links Differential revisions to paths they affect,
  * so Diffusion can efficiently find pending revisions for a given file.
  */
 private function updateAffectedPathTable(DifferentialRevision $revision, DifferentialDiff $diff)
 {
     $repository = $revision->getRepository();
     if (!$repository) {
         // The repository where the code lives is untracked.
         return;
     }
     $path_prefix = null;
     $local_root = $diff->getSourceControlPath();
     if ($local_root) {
         // We're in a working copy which supports subdirectory checkouts (e.g.,
         // SVN) so we need to figure out what prefix we should add to each path
         // (e.g., trunk/projects/example/) to get the absolute path from the
         // root of the repository. DVCS systems like Git and Mercurial are not
         // affected.
         // Normalize both paths and check if the repository root is a prefix of
         // the local root. If so, throw it away. Note that this correctly handles
         // the case where the remote path is "/".
         $local_root = id(new PhutilURI($local_root))->getPath();
         $local_root = rtrim($local_root, '/');
         $repo_root = id(new PhutilURI($repository->getRemoteURI()))->getPath();
         $repo_root = rtrim($repo_root, '/');
         if (!strncmp($repo_root, $local_root, strlen($repo_root))) {
             $path_prefix = substr($local_root, strlen($repo_root));
         }
     }
     $changesets = $diff->getChangesets();
     $paths = array();
     foreach ($changesets as $changeset) {
         $paths[] = $path_prefix . '/' . $changeset->getFilename();
     }
     // Mark this as also touching all parent paths, so you can see all pending
     // changes to any file within a directory.
     $all_paths = array();
     foreach ($paths as $local) {
         foreach (DiffusionPathIDQuery::expandPathToRoot($local) as $path) {
             $all_paths[$path] = true;
         }
     }
     $all_paths = array_keys($all_paths);
     $path_ids = PhabricatorRepositoryCommitChangeParserWorker::lookupOrCreatePaths($all_paths);
     $table = new DifferentialAffectedPath();
     $conn_w = $table->establishConnection('w');
     $sql = array();
     foreach ($path_ids as $path_id) {
         $sql[] = qsprintf($conn_w, '(%d, %d, %d, %d)', $repository->getID(), $path_id, time(), $revision->getID());
     }
     queryfx($conn_w, 'DELETE FROM %T WHERE revisionID = %d', $table->getTableName(), $revision->getID());
     foreach (array_chunk($sql, 256) as $chunk) {
         queryfx($conn_w, 'INSERT INTO %T (repositoryID, pathID, epoch, revisionID) VALUES %Q', $table->getTableName(), implode(', ', $chunk));
     }
 }
$args = new PhutilArgumentParser($argv);
$args->setSynopsis(<<<EOSYNOPSIS
**clear_repository_symbols.php** [__options__] __repository__

  Clear repository symbols.
EOSYNOPSIS
);
$args->parseStandardArguments();
$args->parse(array(array('name' => 'repository', 'wildcard' => true)));
$identifiers = $args->getArg('repository');
if (count($identifiers) !== 1) {
    $args->printHelpAndExit();
}
$identifier = head($identifiers);
$repository = id(new PhabricatorRepositoryQuery())->setViewer(PhabricatorUser::getOmnipotentUser())->withIdentifiers($identifiers)->executeOne();
if (!$repository) {
    echo tsprintf("%s\n", pht('Repository "%s" does not exist.', $identifier));
    exit(1);
}
$input = file_get_contents('php://stdin');
$normalized = array();
foreach (explode("\n", trim($input)) as $path) {
    // Emulate the behavior of the symbol generation scripts.
    $normalized[] = '/' . ltrim($path, './');
}
$paths = PhabricatorRepositoryCommitChangeParserWorker::lookupOrCreatePaths($normalized);
$symbol = new PhabricatorRepositorySymbol();
$conn_w = $symbol->establishConnection('w');
foreach (array_chunk(array_values($paths), 128) as $chunk) {
    queryfx($conn_w, 'DELETE FROM %T WHERE repositoryPHID = %s AND pathID IN (%Ld)', $symbol->getTableName(), $repository->getPHID(), $chunk);
}
    if (strlen($name) > 128) {
        throw new Exception("Symbol name '{$name}' defined on line #{$line_no} is too long, maximum " . "symbol name length is 128 characters.");
    }
    if (strlen($type) > 12) {
        throw new Exception("Symbol type '{$type}' defined on line #{$line_no} is too long, maximum " . "symbol type length is 12 characters.");
    }
    if (strlen($lang) > 32) {
        throw new Exception("Symbol language '{$lang}' defined on line #{$line_no} is too long, " . "maximum symbol language length is 32 characters.");
    }
    if (!strlen($path) || $path[0] != 0) {
        throw new Exception("Path '{$path}' defined on line #{$line_no} is invalid. Paths should be " . "begin with '/' and specify a path from the root of the project, like " . "'/src/utils/utils.php'.");
    }
    $symbols[] = array('name' => $name, 'type' => $type, 'lang' => $lang, 'line' => $line_number, 'path' => $path);
}
echo "Looking up path IDs...\n";
$path_map = PhabricatorRepositoryCommitChangeParserWorker::lookupOrCreatePaths(ipull($symbols, 'path'));
$symbol = new PhabricatorRepositorySymbol();
$conn_w = $symbol->establishConnection('w');
echo "Preparing queries...\n";
$sql = array();
foreach ($symbols as $dict) {
    $sql[] = qsprintf($conn_w, '(%d, %s, %s, %s, %d, %d)', $project->getID(), $dict['name'], $dict['type'], $dict['lang'], $dict['line'], $path_map[$dict['path']]);
}
echo "Purging old symbols...\n";
queryfx($conn_w, 'DELETE FROM %T WHERE arcanistProjectID = %d', $symbol->getTableName(), $project->getID());
echo "Loading " . number_format(count($sql)) . " symbols...\n";
foreach (array_chunk($sql, 128) as $chunk) {
    queryfx($conn_w, 'INSERT INTO %T
      (arcanistProjectID, symbolName, symbolType, symbolLanguage, lineNumber,
        pathID) VALUES %Q', $symbol->getTableName(), implode(', ', $chunk));
}