diff --git a/LIBRARY_GUIDE.md b/LIBRARY_GUIDE.md new file mode 100644 index 0000000..bc58e35 --- /dev/null +++ b/LIBRARY_GUIDE.md @@ -0,0 +1,195 @@ +# PartiQL Graphviz Visualization Library Maintenance Guide + +This guide provides information for the PartiQL team on how to maintain and extend the PartiQL Graphviz visualization library. + +## How to Add New Nodes + +When adding support for new AST node types, follow these steps: + +1. **Identify the node type**: Determine which PartiQL AST node type you need to add support for. + +2. **Create a conversion method**: Add a new method following the naming pattern `convert[NodeType]ToNode()`. For example: + + ```java + private Node convert[NewNodeType]ToNode([NewNodeType] newNode) { + Node newNodeNode = createNode(newNode).with(label("[NewNodeType]")); + + // Add links to child nodes + // For each child node or property that should be visualized: + newNodeNode = newNodeNode.link(to(convertChildToNode(newNode.getChild())).with(Label.of("childName"))); + + return newNodeNode; + } + ``` + +3. **Update the parent method**: Add a new case to the appropriate switch statement in the parent method that handles this type of node. For example, if adding a new expression type: + + ```java + private Node convertExprToNode(Expr expr) { + return switch (expr) { + // Existing cases... + case [NewExprType] newExprType -> convert[NewExprType]ToNode(newExprType); + default -> throw new IllegalArgumentException("Unsupported expression: " + expr); + }; + } + ``` + +4. **Test the visualization**: Create a test query that uses the new node type and verify that it renders correctly. + +### How to Work with Snapshot Testing + +The library uses the [java-snapshot-testing](https://github.com/origin-energy/java-snapshot-testing) framework to perform snapshot testing. This approach allows you to: + +1. Generate a visual representation of a PartiQL query +2. Compare it against a previously saved "snapshot" to detect any changes + +#### Setting Up Snapshot Tests + +1. **Add the JUnit 5 extension**: + ```java + @ExtendWith({SnapshotExtension.class}) + public class AstToQueryTest { + private Expect expect; + // Tests go here + } + ``` + +2. **Create a test method**: + ```java + @Test + public void testIntLiteral() { + Graph graph = new AstToGraph().convertQueryToGraph("12345"); + expect.toMatchSnapshot(graph); + } + ``` + +## How to Debug + +When debugging visualization issues: + +1. **Start with simple queries**: Begin with minimal queries that use the specific node type you're working with. + +2. **Use the debugger**: Step through the code using your IDE's debugger to inspect the structure of both the AST and the resulting Graphviz graph. + +3. **Set breakpoints**: Place breakpoints in key methods like `convertQueryToGraph()` to examine how nodes are being processed. + +4. **Inspect object structures**: Use the debugger's variable inspection features to examine the properties of AST nodes and the corresponding Graphviz nodes being created. + +5. **Watch the graph construction**: Step through the code to see how nodes are connected with edges and how the graph is built incrementally. + +6. **Start with simple queries**: Begin with minimal queries that use the specific node type you're working with. + +7. **Incremental testing**: Add complexity to your test queries gradually to isolate issues. + +8. **Add debug labels**: Temporarily add more detailed labels to nodes to show internal state: + + ```java + // Instead of just the node type + Node node = createNode(expr).with(label("ExprOperator: " + expr.getSymbol() + " [details: " + additionalInfo + "]")); + ``` + + + +## How to Think About Visualizing Nodes + +When designing visualizations for AST nodes: + +1. **Hierarchical structure**: Visualize the AST as a top-down hierarchy, with the root node at the top. + +2. **Node labeling**: Use clear, concise labels that convey the node's type and essential information. + +3. **Edge labeling**: Label edges to show the relationship between nodes (e.g., "lhs", "rhs", "condition"). + +4. **Visual consistency**: Maintain consistent styling for similar node types. + +5. **Information density**: Balance between showing enough information and avoiding visual clutter. + +6. **Node grouping**: Consider grouping related nodes (e.g., all items in a SELECT list) under a common parent. + +7. **Special characters**: Remember to escape special characters in labels using the `escapeGraphvizLabel()` method. + +## Common Graphviz Classes and Functions + +### Core Classes + +- **`Graph`**: Represents the entire graph structure. + ```java + Graph g = graph("Ast Graph").directed(); + ``` + +- **`Node`**: Represents a node in the graph. + ```java + Node node = node("nodeName").with(label("Node Label")); + ``` + +### Node Creation and Styling + +- **`createNode(AstNode ast)`**: Creates a node with a unique identifier based on the AST node's class name. + ```java + Node node = createNode(astNode); + ``` + +- **`label(String text)`**: Sets the display label for a node. + ```java + node.with(label("Label Text")); + ``` + +- **`Color`**: Defines colors for nodes and edges. + ```java + node.with(Color.RED); + ``` + +- **`Style`**: Sets styling attributes for nodes. + ```java + node.with(Style.FILLED, Style.lineWidth(2)); + ``` + +### Edge Creation and Styling + +- **`link(Node target)`**: Creates an edge from one node to another. + ```java + sourceNode.link(targetNode); + ``` + +- **`to(Node target)`**: Used with `link()` to create edges with labels. + ```java + sourceNode.link(to(targetNode).with(Label.of("edgeLabel"))); + ``` + +### Graph Rendering + +- **`convertGraphToDot(Graph graph, String path)`**: Saves the graph as a DOT file. + ```java + astToGraph.convertGraphToDot(graph, "output.dot"); + ``` + +- **`convertDotToPng(Graph graph, String path, double scale)`**: Renders the graph as a PNG image. + ```java + astToGraph.convertDotToPng(graph, "output.png", 2.0); + ``` + +- **`convertDotToSvg(Graph graph, String path, double scale)`**: Renders the graph as an SVG image. + ```java + astToGraph.convertDotToSvg(graph, "output.svg", 2.0); + ``` + +### Utility Functions + +- **`escapeGraphvizLabel(String text)`**: Graphviz uses the DOT language to define graphs, which has its own syntax and special characters. + When including text in node or edge labels, certain characters must be escaped to prevent them from being + interpreted as part of the DOT language syntax rather than as literal characters in the label text. + +Use the `escapeGraphvizLabel()` method for any text that might contain special characters, especially when displaying: + +- Operator symbols (`>`, `<`, `=`, etc.) +- User-defined identifiers that might contain special characters + + ```java + String safeLabel = escapeGraphvizLabel("label"); + ``` + +- **`getIdentifierString(Identifier.Simple simple)`**: Formats identifiers with appropriate quoting. + ```java + String idStr = getIdentifierString(identifier); + ``` + diff --git a/README.md b/README.md index 3b81e29..74ba95d 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,7 @@ Graphviz is a commonly used open source graph visualization library that we can + ## Local Build *** **Pre-requisite:** @@ -25,6 +26,25 @@ $ git clone https://github.com/partiql/partiql-graphviz-java.git ## Usage *** +The PartiQL Graphviz visualization tool allows you to easily convert PartiQL queries into visual graph representations. + +To use this functionality, first create an instance of the `AstToGraph` class. +You can then convert a PartiQL query string directly to a graph using `convertQueryToGraph(String query)`, +or if you already have a parsed AST statement, use `convertAstToGraph(Statement statement)`. + +Once you have the graph, you can export it in various formats: +- Save it as a DOT file with `convertGraphToDot(graph, filePath)` +- Render it as a PNG image using `convertDotToPng(graph, filePath, scale)` +- Create an SVG with `convertDotToSvg(graph, filePath, scale)` + +The `scale` parameter allows you to adjust the size of the output image. + + +This visualization capability is particularly useful for understanding complex query structures, debugging query issues, or educational purposes to demonstrate how PartiQL parses different query constructs. +The generated visual representation clearly shows the hierarchical structure of the query, including SELECT clauses, FROM sources, WHERE conditions, and other query components with their relationships. + + + ### Creating and Visualizing Graphs NOTE: All the example code is written in java ```java @@ -41,10 +61,12 @@ public class Query { // Define output file paths String dotFilePath = "ast_graph.dot"; String pngFilePath = "ast_graph.png"; + String svgFilePath = "ast_graph.svg"; - // Save the graph in DOT and PNG formats + // Save the graph in DOT, PNG and SVG formats astToGraph.convertGraphToDot(graph, dotFilePath); - astToGraph.convertDotToPng(graph, pngFilePath); + astToGraph.convertDotToPng(graph, pngFilePath, 2.0); + astToGraph.convertDotToSvg(graph, svgFilePath, 2.0); } } ``` @@ -64,8 +86,26 @@ Statement statement = parseResult.statements.get(0); Graph graph = astToGraph.convertAstToGraph(statement); ``` +### Example Query + +``` +Query: SELECT a,b,c FROM t WHERE d +``` + +### Example Graph +![img_2.png](img_2.png) + +### Future Enhancements +*** + +TODO: +- Implement remaining AST nodes that are currently not handled by the visualization system +- Visualization of the PartiQL plan +- Introduce additional customization options for node appearance, edge styles, and color schemes +- Provide configuration parameters to control visualization verbosity, allowing for more compact graph representations when desired +- Integrate the PartiQL logo into generated visualizations ## Contributing ***