diff --git a/scripts/symbols/clear_project_symbols.php b/scripts/symbols/clear_project_symbols.php new file mode 100755 index 0000000000..cd1df72f2e --- /dev/null +++ b/scripts/symbols/clear_project_symbols.php @@ -0,0 +1,48 @@ +#!/usr/bin/env php +loadOneWhere( + 'name = %s', $argv[1]); +if (!$project) { + throw new Exception('No such arcanist project.'); +} + +$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 arcanistProjectID = %d AND pathID IN (%Ld)', + $symbol->getTableName(), + $project->getID(), + $chunk); +} diff --git a/scripts/symbols/import_project_symbols.php b/scripts/symbols/import_project_symbols.php index 4ff2a3f2cd..b09fc6f377 100755 --- a/scripts/symbols/import_project_symbols.php +++ b/scripts/symbols/import_project_symbols.php @@ -1,172 +1,179 @@ #!/usr/bin/env php setSynopsis(<<parseStandardArguments(); $args->parse( array( + array( + 'name' => 'no-purge', + 'help' => 'Do not clear all symbols for this project before '. + 'uploading new symbols. Useful for incremental updating.', + ), array( 'name' => 'more', 'wildcard' => true, ), )); $more = $args->getArg('more'); if (count($more) !== 1) { $args->printHelpAndExit(); } $project_name = head($more); $project = id(new PhabricatorRepositoryArcanistProject())->loadOneWhere( 'name = %s', $project_name); if (!$project) { // TODO: Provide a less silly way to do this explicitly, or just do it right // here. echo "Project '{$project_name}' is unknown. Upload a diff to implicitly ". "create it.\n"; exit(1); } echo "Parsing input from stdin...\n"; $input = file_get_contents('php://stdin'); $input = trim($input); $input = explode("\n", $input); $symbols = array(); foreach ($input as $key => $line) { $line_no = $key + 1; $matches = null; $ok = preg_match( '/^((?P[^ ]+)? )?(?P[^ ]+) (?P[^ ]+) '. '(?P[^ ]+) (?P\d+) (?P.*)$/', $line, $matches); if (!$ok) { throw new Exception( "Line #{$line_no} of input is invalid. Expected five or six ". "space-delimited fields: maybe symbol context, symbol name, symbol ". "type, symbol language, line number, path. ". "For example:\n\n". "idx function php 13 /path/to/some/file.php\n\n". "Actual line was:\n\n". "{$line}"); } if (empty($matches['context'])) { $matches['context'] = ''; } $context = $matches['context']; $name = $matches['name']; $type = $matches['type']; $lang = $matches['lang']; $line_number = $matches['line']; $path = $matches['path']; if (strlen($context) > 128) { throw new Exception( "Symbol context '{$context}' defined on line #{$line_no} is too long, ". "maximum symbol context length is 128 characters."); } 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( 'ctxt' => $context, '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, %s, %d, %d)', $project->getID(), $dict['ctxt'], $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()); +if (!$args->getArg('no-purge')) { + 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, symbolContext, symbolName, symbolType, symbolLanguage, lineNumber, pathID) VALUES %Q', $symbol->getTableName(), implode(', ', $chunk)); } echo "Done.\n";