TL;DR , RegreSQL brings PostgreSQL’s regression testing methodology to your application queries, catching both correctness bugs and performance regressions before production.
As tricky as it may seem, the common problem with changes in production is the ever-present “AHA” moment when things start slowing down or instantly crash. It is not as easy to test, but there is a wide variation in practice when it comes to testing SQL queries. Some people may pretend to “fix it” by using ORM to make the problem go away. Others consider SQL to be “just glue code” that does not deserve systematic testing. Most agree to integration tests that verify application layer functions, never actually testing whether their queries will survive the next schema change or index modification.
For PostgreSQL development, the project has a robust regression testing suite that has been preventing disasters in core development for decades. The database itself knows how to test SQL systematically – we don’t use the same techniques for our own queries. Enter RegreSQL, a tool originally created by Dmitry Fontaine The art of PostgreSQL The book (which is excellent for understanding and mastering PostgreSQL as a database system), is designed to bring the same regression testing framework to our application queries.
I’ve been trying to use it for a while, but have given up several times due to missing features and limitations. So far. I decided to spend the time necessary to pursue the project and take it to the next level.
Introduction#
RegreSql The promise starts with the biggest strengths and perceived weaknesses of SQL queries. They’re just strings. And unless you use something like SQLC (for Go), pg’OCaml, or Rust’s SQLX toolkit, which gives you compile-time checking, your queries are only valid when they are executed. Which in the best case means either a usually slow test suite or integration tests, in the worst case only deployed. ORM is another possibility – separating SQL completely (but more on that later).
But even with compile-time checking, you’re only checking for one class of problems: schema mismatches. What about changes in behavior after schema migration or performance regression? How about understanding whether your optimizations actually made things faster or shifted the problem elsewhere?
This is where RegreSQL comes in. Instead of trying to convert SQL into something else, RegreSQL embraces the “SQL as strings” reality and applies the same testing methodology that PostgreSQL itself uses: regression testing. You write (or generate – continue reading) your SQL queries, provide input data, and RegreSQL verifies that future changes do not break those expectations.
The features don’t stop there though – it tracks performance baselines, detects common query plan regressions (like sequential scans), and gives you a framework for systematic experimentation with schema changes and query change management.
Basic Regression Testing#
Enough with the theory. Let’s get straight into the action and see what a sample run of RegreSQL looks like
$ regresql text
Connecting to 'postgres://radim:password123@192.168.139.28/cdstore_test'… ✓
Running regression tests...
✓ album-by-artist_list-albums-by-artist.1.json (0.00s)
✓ album-by-artist_list-albums-by-artist.2.json (0.00s)
✓ album-tracks_list-tracks-by-albumid.2.json (0.00s)
✓ album-tracks_list-tracks-by-albumid.1.json (0.00s)
✓ artist_top-artists-by-album.1.json (0.00s)
✓ genre-topn_genre-top-n.top-1.json (0.00s)
✓ genre-topn_genre-top-n.top-3.json (0.00s)
✓ genre-tracks_tracks-by-genre.json (0.00s)
Results: 8 passed, 0 failed, 8 skipped (0.00s)
In this example based on the Chinook database (as originally used in the book The Art of PostgreSQL), RegreSQL scans the current directory (or the one provided by -C /path/to/project) For *.sql Tries to run all queries against the files and configured PostgreSQL connection.
Different files can contain single or multiple sql queries. like the following example
-- name: top-artists-by-album
-- Get the list of the N artists with the most albums
SELECT
artist.name,
count(*) AS albums
FROM
artist
LEFT JOIN album USING (artist_id)
GROUP BY
artist.name
ORDER BY
albums DESC
LIMIT :n;
The syntax for queries supports both positional arguments (e.g. $1 known from libpq library) or (preferred) psql style variable (:varnameEach identified query (not a file) is executed 0..N times based on a number of predefined plans and verified for expected results – verifying the expected data matches the returned data. Support for managing SQL files is available separately with https://github.com/boringSQL/queries (Go version only for now,
This tells you what the basic RegreSQL tool has to offer – change your schema, refactor the query, run regresql test And immediately see what’s broken. The test suite now has the ability to catch regressions before they are committed/shipped. The current version, built on top of this, gives you a better console formatter instead of TAP style output, as well as jUnit, JSON and GitHub action formatter for better integration into your CI/CD pipelines.
Performance Regression Testing#
Basic regression testing catches correctness issues – wrong results, broken queries, schema mismatches. But there is another set of production issues that this leaves out. Performance Regression. No matter how unbelievable it may sound, but queries are deployed without proper indexes, or they change over time. Simple solutions – for both handwritten SQL or ORM code – can be switched from milliseconds to seconds. You add an index that helps one query, but doesn’t help another query. You modify the conditions and accidentally force a sequential scan of millions of rows. This is where the pain occurs.
RegreSQL addresses this by tracking correctness as well as performance baselines. Once the baseline is ready
$ regresql baseline
Connecting to 'postgres://appuser:password123@192.168.139.28/cdstore_test'… ✓
Creating baselines directory: regresql/baselines
Creating directory 'regresql/baselines'
Creating baselines for queries:
./
Created baseline: album-by-artist_list-albums-by-artist.1.json
Created baseline: album-by-artist_list-albums-by-artist.2.json
Created baseline: album-tracks_list-tracks-by-albumid.1.json
Created baseline: album-tracks_list-tracks-by-albumid.2.json
Created baseline: artist_top-artists-by-album.1.json
Created baseline: genre-topn_genre-top-n.top-1.json
Created baseline: genre-topn_genre-top-n.top-3.json
Created baseline: genre-tracks_tracks-by-genre.json
Baselines have been created successfully!
Baseline files are stored in: regresql/baselines
The test command not only tests the captured time regressions, but also detects common bad patterns in query execution plans. For now it provides warnings to detect sequential scans – both on their and/or nested loops and multiple sort operations. I believe this alone can provide valuable insight and reduce accidents in production. This is also a place where further development of RegreSQL will take place.
To demonstrate this, let’s review the test output with baselines.
Connecting to 'postgres://appuser:password123@192.168.139.28/cdstore_test'… ✓
Running regression tests...
✓ album-by-artist_list-albums-by-artist.1.json (0.00s)
✓ album-by-artist_list-albums-by-artist.2.json (0.00s)
✓ album-by-artist_list-albums-by-artist.1.cost (22.09 <= 22.09 * 110%) (0.00s)
⚠️ Sequential scan detected on table 'artist'
Suggestion: Consider adding an index if this table is large or this query is frequently executed
⚠️ Nested loop join with sequential scan detected
Suggestion: Add index on join column to avoid repeated sequential scans
✓ album-by-artist_list-albums-by-artist.2.cost (22.09 <= 22.09 * 110%) (0.00s)
⚠️ Sequential scan detected on table 'artist'
Suggestion: Consider adding an index if this table is large or this query is frequently executed
⚠️ Nested loop join with sequential scan detected
Suggestion: Add index on join column to avoid repeated sequential scans
✓ album-tracks_list-tracks-by-albumid.1.json (0.00s)
✓ album-tracks_list-tracks-by-albumid.2.json (0.00s)
✓ album-tracks_list-tracks-by-albumid.1.cost (8.23 <= 8.23 * 110%) (0.00s)
✓ album-tracks_list-tracks-by-albumid.2.cost (8.23 <= 8.23 * 110%) (0.00s)
✓ artist_top-artists-by-album.1.json (0.00s)
✓ artist_top-artists-by-album.1.cost (35.70 <= 35.70 * 110%) (0.00s)
⚠️ Multiple sequential scans detected on tables: album, artist
Suggestion: Review query and consider adding indexes on filtered/joined columns
✓ genre-topn_genre-top-n.top-1.json (0.00s)
✓ genre-topn_genre-top-n.top-3.json (0.00s)
✓ genre-topn_genre-top-n.top-1.cost (6610.59 <= 6610.59 * 110%) (0.00s)
⚠️ Multiple sequential scans detected on tables: genre, artist
Suggestion: Review query and consider adding indexes on filtered/joined columns
⚠️ Multiple sort operations detected (2 sorts)
Suggestion: Consider composite indexes for ORDER BY clauses to avoid sorting
⚠️ Nested loop join with sequential scan detected
Suggestion: Add index on join column to avoid repeated sequential scans
✓ genre-topn_genre-top-n.top-3.cost (6610.59 <= 6610.59 * 110%) (0.00s)
⚠️ Multiple sequential scans detected on tables: artist, genre
Suggestion: Review query and consider adding indexes on filtered/joined columns
⚠️ Multiple sort operations detected (2 sorts)
Suggestion: Consider composite indexes for ORDER BY clauses to avoid sorting
⚠️ Nested loop join with sequential scan detected
Suggestion: Add index on join column to avoid repeated sequential scans
✓ genre-tracks_tracks-by-genre.json (0.00s)
✓ genre-tracks_tracks-by-genre.cost (37.99 <= 37.99 * 110%) (0.00s)
⚠️ Multiple sequential scans detected on tables: genre, track
Suggestion: Review query and consider adding indexes on filtered/joined columns
Results: 16 passed (0.00s)
As you can see, despite not having a baseline, RegreSQL is able to detect basic bad patterns that must be addressed before queries can be considered “production ready”.
In some cases, detecting sequential scans, or tracking only the query cost baseline, may be considered undesirable, which will lead to false positives. RegreSQL enables this to be addressed by query metadata, as shown below.
-- name: query_name
-- metadata: key1=value1, key2=value2
SELECT ...;
At this point RegreSQL recognizes
notestSkipping query testing altogether (not just cost tracking)nobaselineTo quit cost trackingnoseqscanwarnTo keep track of costs but disable sequential scan warnings- And
difffloattoleranceCost up to failure limit (currently default 10%).
-- name: query_name
-- regresql: notest, nobaseline
-- regresql: noseqscanwarn
-- regresql: difffloattolerance:0.25
-- query that can vary in cost by 20% without being considered a failure
SELECT ...;
ORM enters the room#
ORMs abstract out the SQL, but they still generate it – and the generated SQL may have performance issues that you won’t catch until production. Consider this common scenario: You start with a simple SQLAlchemy query that works fine, then months later add eager loading for related data:
orders = (
session.query(Order)
.filter(Order.user_id == user_id)
.options(
joinedload(Order.user),
joinedload(Order.shipping_address),
selectinload(Order.items) # NEW: Load order items
)
.all()
)
that innocent one selectinload(Order.items) Generates a different query – and without any indexes order_items.order_idIt performs a sequential scan.
RegreSQL can catch this by intercepting ORM-generated SQL using SQLAlchemy’s event system:
@event.listens_for(engine, "before_cursor_execute")
def capture_sql(conn, cursor, statement, *args):
captured_queries.append(statement)
Run your ORM code, capture the SQL, save it as a .sql file, and test it with RegreSQL. Performance baseline testing will flag missing indices before production begins. This is currently experimental, but ORM integration is an important area for future development of RegreSQL.
Test Data Management#
So far we have covered how RegreSQL verifies the correctness of queries and tracks performance regressions. But there is one important condition which we have met only cursorily. Every regression test requires consistent, reproducible data. Change the data, change their cardinality, and your expected results will become meaningless. Your performance baselines slip. Your tests get messed up.
Traditional approaches to generating test data may include
- database dump Become unmanageable – 500MB files that you can’t review, can’t understand, that break with every schema migration, and whose data becomes out of date as production evolves. What version of your test dumps are you using?
- SQL Script Might be better than dump, but still mandatory and difficult to maintain. You end up with INSERT statements scattered across multiple files, manually managing foreign keys, and debugging constraint violations.
- Factories in application code Can work great for integration tests, but we’re testing SQL directly. Do you really want to maintain parallel data generation in your application language just for SQL tests?
- shared test database Synonymous with the classic “works on my machine” problem. State leaks between tests. Parallel execution becomes impossible. Debugging is a nightmare.
We need something that is declarative (what data, not how to insert it), reproducible (same data every time), composable (build complex scenarios from simple pieces), and scalable (from 10 rows to 100,000).
This is where the next improvement to RegreSQL’s fixture system comes in. Think of it as the infrastructure-code for your test data. You describe the data you need in YAML files, and RegreSQL handles the rest – dependencies, cleanup, foreign keys, and even large-scale realistic data generation.
RegreSQL’s fixture system lets you define test data in stored YAML files regresql/fixtures/Here is a simple example
fixture: basic_users
description: a handful of test users
cleanup: rollback
data:
- table: users
rows:
- id: 1
email: alice@example.com
name: Alice Anderson
created_at: 2024-01-15
- id: 2
email: bob@example.com
name: Bob Builder
created_at: 2024-02-20
To use this fixture in your tests, reference it in the query’s plan file (regresql/plans/get-user.yaml) You can only reference fixtures
fixtures:
- basic_users
"1":
email: alice@example.com
"2":
email: bob@example.com
and when you run regresql testThe fixture is automatically loaded before the query is executed, and cleared afterward. No manual setup scripts, no state leakage between tests. But it doesn’t stop with static fixtures. You can use Range when you want to test queries against realistic volume data generator Involved
- Sequence, random integer, decimal, string, UUID, email and name generator
- date_between to generate random timestamps within a range
- Foreign key reference to be able to reuse data from other table’s fixtures
- Range to select value from predefined sources
- go template support
fixture: realistic_orders
generate:
- table: customers
count: 1000
columns:
id:
generator: sequence
start: 1
email:
generator: email
domain: shop.example.com
name:
generator: name
type: full
created_at:
generator: date_between
start: "2023-01-01"
end: "2024-12-31"
- table: orders
count: 5000
columns:
id:
generator: sequence
start: 1
customer_id:
generator: int
min: 1
max: 1000
amount:
generator: decimal
min: 10.00
max: 999.99
precision: 2
order_date:
generator: date_between
start: "2023-01-01"
end: "2024-12-31"
It generates 1,000 customers and 5,000 orders with realistic looking data – name, email, date and quantity that feels like production.
there are also fixtures stackable And can be built on top of each other. For example, if you need to ensure that user fixtures are created before order fixtures, simply declare the dependency (an already planned improvement to avoid ID hard-coding is to include support for automatic foreign-key detection). RegreSQL loads fixtures in dependency order and handles cleanup in reverse.
fixture: orders_with_shipping
depends_on:
- basic_users
data:
- table: orders
rows:
- id: 101
user_id: 1 # References Alice from basic_users
total: 99.99
status: shipped
If the options available for fixtures (manual data or data generator) are not enough, you always have the option of using good old SQL based data generation.
fixture: mixed_setup
description: Combine SQL with YAML and generated data
cleanup: rollback
# SQL executes first (either as file or inline)
sql:
- file: sql/setup_schema.sql
- inline: "INSERT INTO config (key, value) VALUES ('version', '1.0');"
# followed YAML data
data:
- table: users
rows:
- id: 1
email: admin@example.com
# and finally generated data
generate:
- table: orders
count: 100
columns:
id:
generator: sequence
start: 1
user_id:
generator: int
min: 1
max: 1
RegreSQL provides commands to inspect and verify your fixtures
# List all available fixtures
regresql fixtures list
# Show fixture details and dependencies
regresql fixtures show realistic_orders
# Validate fixture definitions
regresql fixtures validate
# Show dependency graph
regresql fixtures deps
# Apply fixture manually (for debugging)
regresql fixtures apply basic_users
The fixture system is designed to transform test data from a maintenance burden into a documented, version-controlled process. Your YAML files become a single source of truth for the data needed for your tests, making it easier to understand test scenarios and maintain test data as the application evolves.
RegreSQL future#
Introducing a new open source project is an ambitious goal, and RegreSQL is just getting started. Despite this, the fork has been in use for almost 2 years. I’m planning further improvements, as well as better documentation and more tutorials, in the coming weeks and months. The project is maintained as part of my BoringSql brand, where it is a key component to the creation of SQL Labs which will (as I fully expect) provide a foundation for its further development.
At the same time RegreSql This is an attempt to give back to the welcoming PostgreSQL community, if possible improve the developer user experience a bit and (maybe maybe) provide another argument against the case that SQL queries are not testable.
RegreSQL is available on GitHub – feel free to open an issue, or send me an email about the project at radim@boringsql.com or connect on LinkedIn.
https://pinnacle-bet.com.mx/
Pinnacle esta lejos de ser el casino habitual que te cautiva con bonificaciones desorbitadas llenos de condiciones imposibles; su planteamiento gira en torno a margenes ajustados, cuotas realmente competitivas y una operativa clara sin enganos.
https://medium.com/@mikefromgidstats/perth-is-for-the-dogs-my-ufc-best-bets-984ceb56f3a9
Hey, if you’re betting the heavily favored fighters just because they represent Australia with a flag, you could just set your bankroll ablaze inside the UFC Perth event instead.
UFC Perth on May 2nd, 2026 featuring a card headlined by significant stand-up chess matches and a blend of established veterans taking on ascending Australian prospects.
https://justpaste.it/pfl-sioux-falls-mjmil
The upcoming PFL Sioux Falls fight night at the Sanford Pentagon delivers a high-level blend of local favorites and established division contenders aiming to lock down their position in the 2026 season.
PFL Sioux Falls is essentially a Logan Storley homecoming celebration masked as an official professional fight night.
UFC stats
PFL heads back to the Pentagon venue this coming Saturday for an event led by hometown star Logan Storley.
Come Saturday, that proximity will be most intensely experienced in the case of Florim Zendeli, a competitor prepared to enter a hostile den to battle the homegrown champion, Logan Storley.
https://dev.to/mikefromgidstats/ufc-pfl-sioux-falls-the-stakes-at-the-sanford-pentagon-8h6
The Sanford Pentagon in Sioux Falls has a unique ability to transforming intense combat take on an strangely personal feel.
The ACA 203 event arrives in Tashkent with a pivotal flyweight top bout and an event that is deeply rooted in the promotion’s regional strength in Uzbekistan.
ACA 203 arrives in Tashkent with a flyweight main event of significant importance and a card that leans heavily into the promotion’s local dominance across Uzbekistan.
Should you be searching for an event where the oddsmakers basically just looked at records and called it a day , ACA 203 in Tashkent is your gold mine .
ACA 203 is not simply another regional show; it is a crossroads .
If you want a fight card where the bookmakers essentially glanced at the win-loss columns and stopped there , ACA 203 taking place in Tashkent is a treasure trove you won’t want to miss.
The air in Tashkent’s Humo Arena this May won’t merely be dense with the roar of a home-biased audience ; instead, it will be burdened by the career survival of fighters who’ve devoted their lives to chasing glory .
ACA 203 represents more than a routine stop on the circuit ; instead, it serves as a career crossroads .
UAE Warriors 70 is not simply another routine regional show. For the Middle East, UAE Warriors is the elite foundry where young fighters are either molded into diamonds or ground down to dust.
https://justpaste.it/uae-warriors-70
If your goal is to profit from regional heavyweights who battle like they’re being charged by the minute UAE Warriors 70 is your playground .
UAE Warriors 70 lands at Abu Dhabi’s Space42 Arena this Friday, May 8 presenting an event dense with regional experts and veteran international fighters chasing a final launchpad to major organizations .
https://telegra.ph/Betting-Analysis-ACA-Young-Eagles-64-05-06
ACA Young Eagles 64, which happens in mid-May 2026 in Grozny is a classic example of a card where the odds frequently misrepresent the gap in skill sets , particularly within the prospect levels of ACA .
UFC ACA Young Eagles 64 Georgy Shakhruramazanov vs Turpal Gediev
ACA Young Eagles 64 is going down in Grozny, Chechnya , and if you’re convinced the oddsmakers truly understand these developmental fighters , then you’re headed for a costly wake-up call .
UFC ACA Young Eagles 64 Georgy Shakhruramazanov vs Turpal Gediev
ACA Young Eagles 64, set to take place in mid-May 2026 stands as a classic illustration of a fight card where the popular narrative regularly fails to reflect the actual gap in capabilities, most notably in the growth stages of the ACA developmental system.
ACA Young Eagles 64 is a card built for grinders, pure and simple.
ACA Young Eagles 64 is not simply another showcase for developing talent; it functions as a career-defining filter with serious consequences.
LFA 232] heads to the famed Foxwoods Resort Casino in Connecticut this Saturday and Sunday with a vacant featherweight title on the line and numerous high-risk area-based showdowns.
UFC LFA 232 fight card
LFA 232 travels to the Foxwoods gaming venue this Saturday and Sunday with the unclaimed 145-pound belt being contested and a handful of high-risk local bouts.
https://www.tumblr.com/mike-from-gidstats/816322217183985664/lfa-232-foxwoods-free-money-and-fading-the-hype
While fair-weather fans are holding out for the UFC event on Saturday, we’ll be at Foxwoods on Friday night to take advantage of significant local market inefficiencies.
UFC LFA 232
A quick look at the trends on GidStats makes it clear that grinders who stay active are a nightmare for big punchers in LFA title fights when the rounds start stacking up. I’m taking Pintos and there’s no turning back.
The next UFC Vegas 117 card is an archetypal Apex event, featuring tight matchups and a few distinct stylistic clashes that offer actionable betting value.
https://differ.blog/p/tactical-breakdown-and-value-analysis-lfa-233-preview-a0d2fc
When it comes to LFA 233, putting money behind proven cardio and defensive wrestling numbers over brute force tends to deliver greater success.
We’re going to Salamanca as we approach LFA 233, and let’s be real, the bookmakers are clearly overrating local fighter stories rather than real strategic substance.
UFC LFA 234 Lucas Fernando vs Leon Soares
Legacy Fighting Alliance is back with LFA 234, presenting a card containing a number of sharp betting angles for those individuals looking past the promotion’s storyline.
The competitors entering the spotlight at LFA 234 understand fully the significance of a commanding performance.
https://white-house-fight-insight.lovable.app/
The Ultimate Fighting Championship White House event goes down in history on June 14th, 2026 — marking the first UFC event taking place on the grounds of the White House in the nation’s capital. Billed officially as UFC Freedom Fights 250, the card is headlined by Ilia Topuria vs. Justin Gaethje for the 155-pound championship, with ex-two-division champion Alex Pereira moving up to heavyweight to square off against Ciryl Gane in the co-main event.
The UFC Fight Night in Baku is the inaugural UFC event in Azerbaijan, a 12-fight card under the UFC Fight Night banner on June 27, 2026, a Saturday at the Baku Crystal Hall.
On June 14th, 2026, the UFC seizes control of the White House’s South Lawn for the most politically fueled fight night the sport has ever seen.