The problem with Redash I’ve seen
Redash is in a lot of companies, and its good at what it does. User writes SQL, get a chart, put the chart on a dashboard. Nothing about that is broken.
I found problems around the SQL.
The same metric gets retyped in query after query. Someone writes cost per contact as a sum over a count. Someone else writes it as an average of a ratio.
Joins get rediscovered every time. Not everyone knows the joins across all the tables.
No row-level security. A saved query returns whatever the warehouse returns. If a regional manager should only see their own region, your options are a separate query per region, or a parameter they can edit.
Hard for non-technical users since it involves raw SQL to get things done.
All these point to one thing. Redash has no idea what the data its dealing with. That was fixed by introducing a semantic model powered by 0sql. Not another fork of Redash but just a python file to get the fix integrated.
What you type instead
A query is one line of business terms:
contact channel, aht mins, escalation rate
Thats the whole query now. Hit the execute and you get a table with 3 columns and visualisation exactly as if you had written the raw SQL yourself.
What happened in between is that something read the line, worked out that handle time and escalation rate live on the contact fact, that contact channel lives on a subchannel dimension two joins away, and wrote this under the hood.
SELECT
T1."contact_channel_id" AS "Contact Channel",
sum(T0."answer_hold_duration_secs" + T0."talk_duration_secs"
+ T0."acw_duration_secs") / nullif(sum(T0."answered_cnt"), 0) / 60.0 AS "AHT Mins",
sum(T0."is_assistance_requested") * 1.0
/ nullif(sum(case when T2."major_transfer_type_desc" <> 'TRANSFER_OUT'
then T0."answered_cnt" else 0 end), 0) AS "Escalation Rate"
FROM cs_contact_f T0
LEFT JOIN cs_contact_subchannel_d T1 ON T0.contact_subchannel_id = T1.contact_subchannel_id
LEFT JOIN cs_transfer_type_d T2 ON T0.transfer_type_id = T2.transfer_type_id
GROUP BY T1."contact_channel_id"Note the escalation rate. It excludes transfer-outs from its denominator, because a contact that was handed off was not escalated by the site that handed it off. That rule was decided once, by whoever wrote the model, and it is now in every query anyone writes. Nobody has to remember it and nobody can forget it.
The generated SQL is not hidden. It shows on the query page under the results, every time. Prefix the line with explain and you get only the SQL, as rows. A semantic layer you cannot see through is a semantic layer nobody trusts.
Example screen:
The split that makes this possible
Redash has an extension point called a query runner. It is the thing that knows how to talk to Postgres, or BigQuery, or Mongo. You register one and a new data source type appears in the UI. We registered one that talks to no database at all.
0sql takes a query and the shape of your tables and gives back SQL text. It has no driver, no connection string and no credentials. Our runner takes that SQL and hands it to a Redash data source that already exists, which runs it the way it runs everything else.
It also means the security story is simple. Your warehouse credentials stay where they already were, in Redash. Nothing new gets access to your data, because the new component cannot reach it.
The part Redash doesn’t have
Row-level security is the reason to do this rather than the nice-to-have at the end.
The model carries a policy. It says that any measure tagged cost-sensitive gets filtered to the call centre regions the caller is allowed to see:
policies:
- name: Regional Cost Visibility
mode: filter_data
triggers: { field_tags: [cost-sensitive] }
context_dimension: Call Center Region
permission_resolution:
source: groups
value_from: tag
tag_key: region
unresolved: deny
bypass: { system_admin: true, project_admin: true }The runner sends the Redash user along with the query. A Redash group named region:AMER arrives as the tag region:AMER, and the policy resolves it.
So the admin runs a saved query and sees three regions. Person who is in that group, opens the same saved query and sees one. Not a different query, not a parameter they could change. The same query, planned differently because of who asked.
The filter is compiled into the statement before it reaches the warehouse:
WHERE LOWER(T1."region") IN ('amer')Someone with no region tag at all gets WHERE 1 = 0 and zero rows, because the policy says unresolved: deny. That default matters more than it looks. The alternative, where an unrecognised caller sees everything, is how data leaks.
Admins bypass it, which is also in the policy rather than in anyone’s code.
Writing the model
The schema is already in Redash. If someone has a working Redash data source, Redash has already introspected their warehouse. Every table, every column, every type, one API call away. We do not need warehouse credentials and we never read a row:
curl -H "Authorization: Key $KEY" "$REDASH/api/data_sources/<id>/schema" > schema.jsonThe model gets written by Claude. The repo ships a prompt. You hand it that file and the schema, and it writes the model: table files, dimensions and measures, joins with their cardinality, policies.
Try it
https://github.com/stratasite/redash-0sql#redash--0sql






