Switching from row-by-row inserts to PostgreSQL’s COPY protocol meaningfully cut import time. Why COPY is fast, how to stream data into it from Python, and two approaches to expanding a JSON blob into a typed row.
Each AST node emits a SQL fragment and bound parameters. How the node hierarchy works, how different field types generate different SQL, and why user input never touches the query string. Covers text/regex/arithmetic; JSONB operators and the count+results CTE are in separate posts.
The Scryfall query language implicitly ANDs adjacent terms — pyparsing does not. This post covers the preprocessing layer built on top: implicit AND injection, query balancing in two languages, and the edge cases that required fixes.
How I evaluated parsing approaches for Scryfall’s query language: why regex and ANTLR were ruled out, how Lark compares to pyparsing, and why pyparsing won.
Why Sylvan Librarian uses Falcon and Bjoern instead of the FastAPI + uvicorn default: a preference for explicit, close-to-vanilla Python over framework magic.
Motivation for building Sylvan Librarian: owning the query language, and the killer feature Scryfall can’t do — arithmetic comparisons across card attributes.