{"id":3244,"date":"2026-05-05T17:54:03","date_gmt":"2026-05-05T09:54:03","guid":{"rendered":"https:\/\/www.dpriver.com\/blog\/?p=3244"},"modified":"2026-05-05T18:16:45","modified_gmt":"2026-05-05T10:16:45","slug":"field-level-permission-checks-for-text-to-sql-systems","status":"publish","type":"post","link":"https:\/\/www.dpriver.com\/blog\/field-level-permission-checks-for-text-to-sql-systems\/","title":{"rendered":"Field-Level Permission Checks for Text-to-SQL Systems"},"content":{"rendered":"<p><strong>Length:<\/strong> About 3,500 words \u00b7 <strong>Reading time:<\/strong> about 16\u201318 minutes<\/p>\n<p>Field-level permission checks for Text-to-SQL systems determine whether a generated SQL query is allowed to access each column it references, not only whether the user can access the table. A safe Text-to-SQL workflow should detect sensitive fields in projections, filters, joins, aggregations, derived expressions, and lineage before the query reaches the database.<\/p>\n<p>This matters because generated SQL often looks harmless at the table level. A user may be allowed to query a <code>customers<\/code> table for basic analytics, but not allowed to select <code>email<\/code>, filter by <code>ssn_last4<\/code>, join on <code>device_id<\/code>, or derive a segment from a restricted health, salary, or financial field. Table permissions alone cannot express these cases clearly enough for production AI data access.<\/p>\n<h2>Short Answer<\/h2>\n<p>Text-to-SQL systems need field-level permission checks because the LLM generates the exact SQL shape at runtime. The system must inspect the generated query before execution and ask:<\/p>\n<blockquote>\n<p>Which fields does this query read, expose, filter on, join with, aggregate, derive, or pass into downstream outputs \u2014 and is this user allowed to use those fields for this purpose?<\/p>\n<\/blockquote>\n<p>A practical field-level permission check should:<\/p>\n<ol>\n<li>parse the SQL;<\/li>\n<li>bind tables, aliases, CTEs, and columns to catalog metadata;<\/li>\n<li>identify all field usages, not only selected output columns;<\/li>\n<li>classify sensitive fields such as PII, financial data, HR data, credentials, or regulated attributes;<\/li>\n<li>evaluate policies using user, role, purpose, environment, and query shape;<\/li>\n<li>return a structured decision: <code>allow<\/code>, <code>warn<\/code>, <code>deny<\/code>, or <code>approval_required<\/code>;<\/li>\n<li>write an audit record explaining which fields and policies affected the decision.<\/li>\n<\/ol>\n<h2>Key Takeaways<\/h2>\n<ul>\n<li>Table-level access is not enough for production Text-to-SQL because sensitive data risk often lives at the column level.<\/li>\n<li>A generated query can expose restricted fields directly in <code>SELECT<\/code>, indirectly through filters, joins, aggregations, CASE expressions, or derived outputs.<\/li>\n<li>Field-level permission checks require catalog-aware SQL semantic analysis, not string matching.<\/li>\n<li>A policy engine should evaluate the query against user role, purpose, field labels, environment, and usage context.<\/li>\n<li>Useful decisions are explicit: <code>allow<\/code>, <code>warn<\/code>, <code>deny<\/code>, or <code>approval_required<\/code>.<\/li>\n<li>Field-level permissions are a bridge between SQL semantic validation, column-level lineage, LLM SQL Guard architecture, and SQL governance readiness assessments.<\/li>\n<\/ul>\n<h2>Why Table-Level Permissions Are Not Enough<\/h2>\n<p>Many database permission models begin with table access:<\/p>\n<pre><code class=\"language-text\">analyst can SELECT from analytics.customers\nanalyst can SELECT from analytics.orders\nanalyst cannot SELECT from raw.payment_cards\n<\/code><\/pre>\n<p>That is useful, but Text-to-SQL creates a more precise problem. A generated query may access an allowed table in an unsafe way.<\/p>\n<p>For example:<\/p>\n<pre><code class=\"language-sql\">SELECT\n  customer_id,\n  email,\n  phone,\n  lifetime_value\nFROM analytics.customers\nWHERE country = 'US';\n<\/code><\/pre>\n<p>A business analyst may be allowed to analyze customers by country, segment, or lifetime value. But the same user may not be allowed to retrieve raw email addresses or phone numbers. If the system only checks table access, the query may appear acceptable because <code>analytics.customers<\/code> is allowed.<\/p>\n<p>Field-level permission checking asks a more specific question:<\/p>\n<pre><code class=\"language-text\">Can this user access analytics.customers.customer_id?  yes\nCan this user access analytics.customers.email?        no\nCan this user access analytics.customers.phone?        no\nCan this user access analytics.customers.lifetime_value? maybe, depending on role and purpose\n<\/code><\/pre>\n<p>For Text-to-SQL, this distinction matters because the user did not hand-write the SQL. The model may choose fields that the user did not explicitly request, use <code>SELECT *<\/code>, or include sensitive fields because they seem useful for answering the prompt. The guard layer must check the actual generated SQL, not only the user&#8217;s natural-language intent.<\/p>\n<h2>Where Sensitive Fields Hide in SQL<\/h2>\n<p>A common mistake is to check only the final <code>SELECT<\/code> list. That misses many real permission risks.<\/p>\n<p>Sensitive fields can appear in several places.<\/p>\n<table>\n<thead>\n<tr>\n<th>SQL location<\/th>\n<th>Example<\/th>\n<th>Why it matters<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>Projection<\/td>\n<td><code>SELECT email<\/code><\/td>\n<td>The field is directly exposed in the result.<\/td>\n<\/tr>\n<tr>\n<td>Filter<\/td>\n<td><code>WHERE ssn_last4 = '1234'<\/code><\/td>\n<td>The field affects which rows are returned, even if not displayed.<\/td>\n<\/tr>\n<tr>\n<td>Join<\/td>\n<td><code>JOIN devices d ON c.device_id = d.device_id<\/code><\/td>\n<td>The field may connect identity, behavior, or regulated data.<\/td>\n<\/tr>\n<tr>\n<td>Aggregation<\/td>\n<td><code>COUNT(DISTINCT email)<\/code><\/td>\n<td>Raw values may not be exposed, but the sensitive field is used.<\/td>\n<\/tr>\n<tr>\n<td>Grouping<\/td>\n<td><code>GROUP BY medical_condition<\/code><\/td>\n<td>The output may reveal restricted categories.<\/td>\n<\/tr>\n<tr>\n<td>Ordering<\/td>\n<td><code>ORDER BY salary DESC<\/code><\/td>\n<td>Restricted fields can affect ranking.<\/td>\n<\/tr>\n<tr>\n<td>CASE expression<\/td>\n<td><code>CASE WHEN income &gt; 200000 THEN 'high'<\/code><\/td>\n<td>A derived output can reveal sensitive source information.<\/td>\n<\/tr>\n<tr>\n<td>Window function<\/td>\n<td><code>ROW_NUMBER() OVER (PARTITION BY customer_id ORDER BY credit_score)<\/code><\/td>\n<td>Partition\/order fields can carry policy implications.<\/td>\n<\/tr>\n<tr>\n<td>CTE or subquery<\/td>\n<td><code>WITH pii AS (...) SELECT count(*) FROM pii<\/code><\/td>\n<td>Sensitive access can be hidden in intermediate scopes.<\/td>\n<\/tr>\n<tr>\n<td><code>SELECT *<\/code><\/td>\n<td><code>SELECT * FROM customers<\/code><\/td>\n<td>The selected fields depend on catalog metadata, not visible SQL text alone.<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>A Text-to-SQL system should treat these as different usage roles. Selecting <code>email<\/code> is not the same as filtering on <code>email<\/code>, and aggregating <code>salary<\/code> is not the same as returning every salary value. But all of them are field usage and should be visible to the policy engine.<\/p>\n<h2>Example 1: Direct Sensitive Field Exposure<\/h2>\n<p>A user asks:<\/p>\n<pre><code class=\"language-text\">Show the top customers in California.\n<\/code><\/pre>\n<p>The LLM generates:<\/p>\n<pre><code class=\"language-sql\">SELECT\n  customer_id,\n  full_name,\n  email,\n  phone,\n  total_spend\nFROM analytics.customers\nWHERE state = 'CA'\nORDER BY total_spend DESC\nLIMIT 50;\n<\/code><\/pre>\n<p>The query is syntactically valid. The table may be allowed. But the model added contact fields that the user did not need.<\/p>\n<p>A field-level permission result should identify the specific fields and the decision:<\/p>\n<pre><code class=\"language-json\">{\n  &quot;decision&quot;: &quot;deny&quot;,\n  &quot;reason&quot;: &quot;Query exposes PII fields not allowed for analyst role.&quot;,\n  &quot;field_access&quot;: [\n    {\n      &quot;field&quot;: &quot;analytics.customers.email&quot;,\n      &quot;usage&quot;: &quot;projection&quot;,\n      &quot;labels&quot;: [&quot;PII&quot;, &quot;contact&quot;],\n      &quot;policy&quot;: &quot;deny_pii_projection_for_analyst&quot;,\n      &quot;effect&quot;: &quot;deny&quot;\n    },\n    {\n      &quot;field&quot;: &quot;analytics.customers.phone&quot;,\n      &quot;usage&quot;: &quot;projection&quot;,\n      &quot;labels&quot;: [&quot;PII&quot;, &quot;contact&quot;],\n      &quot;policy&quot;: &quot;deny_pii_projection_for_analyst&quot;,\n      &quot;effect&quot;: &quot;deny&quot;\n    }\n  ],\n  &quot;recommended_action&quot;: &quot;Remove email and phone, or request approval under a permitted role.&quot;\n}\n<\/code><\/pre>\n<p>This is more useful than a generic \u201cpermission denied\u201d error. It tells the application, reviewer, or repair loop exactly which fields caused the decision.<\/p>\n<h2>Example 2: Sensitive Field Used in a Filter<\/h2>\n<p>Not all sensitive access appears in the output.<\/p>\n<pre><code class=\"language-sql\">SELECT\n  customer_id,\n  total_spend\nFROM analytics.customers\nWHERE ssn_last4 = '1234';\n<\/code><\/pre>\n<p>The result does not display <code>ssn_last4<\/code>, but the query uses it to select rows. For many organizations, filtering by a highly sensitive identifier is still restricted. It can reveal whether a person exists in a dataset or allow targeted lookup.<\/p>\n<p>A practical policy may distinguish projection from filtering:<\/p>\n<pre><code class=\"language-yaml\">policies:\n  - id: deny_ssn_filter_for_analyst\n    type: field_access\n    effect: deny\n    when:\n      role: analyst\n      field_labels_any: [government_identifier]\n      usage_any: [filter]\n<\/code><\/pre>\n<p>The decision might be:<\/p>\n<pre><code class=\"language-json\">{\n  &quot;decision&quot;: &quot;deny&quot;,\n  &quot;matched_policies&quot;: [&quot;deny_ssn_filter_for_analyst&quot;],\n  &quot;reason&quot;: &quot;Analyst role cannot filter customers by government identifiers.&quot;,\n  &quot;field_access&quot;: [\n    {\n      &quot;field&quot;: &quot;analytics.customers.ssn_last4&quot;,\n      &quot;usage&quot;: &quot;filter&quot;,\n      &quot;labels&quot;: [&quot;PII&quot;, &quot;government_identifier&quot;],\n      &quot;effect&quot;: &quot;deny&quot;\n    }\n  ]\n}\n<\/code><\/pre>\n<p>This is why a field-level permission engine needs semantic analysis. A string search for <code>ssn<\/code> is not enough. The system must resolve the referenced column to the catalog, know its labels, and understand where it appears in the query.<\/p>\n<h2>Example 3: Aggregation May Be Allowed When Raw Values Are Not<\/h2>\n<p>Some policies allow aggregate analysis while denying raw field exposure.<\/p>\n<p>For example, a compensation analyst may not be allowed to list individual salaries:<\/p>\n<pre><code class=\"language-sql\">SELECT employee_id, salary\nFROM hr.employees;\n<\/code><\/pre>\n<p>But the same user may be allowed to query an aggregate:<\/p>\n<pre><code class=\"language-sql\">SELECT department, AVG(salary) AS avg_salary\nFROM hr.employees\nGROUP BY department;\n<\/code><\/pre>\n<p>Even then, the query may require safeguards: minimum group size, approved purpose, row-level filters, masking, or human approval.<\/p>\n<p>A field-level permission check should therefore preserve usage context:<\/p>\n<pre><code class=\"language-json\">{\n  &quot;field&quot;: &quot;hr.employees.salary&quot;,\n  &quot;usage&quot;: &quot;aggregation_input&quot;,\n  &quot;aggregation&quot;: &quot;AVG&quot;,\n  &quot;output_column&quot;: &quot;avg_salary&quot;,\n  &quot;labels&quot;: [&quot;HR&quot;, &quot;compensation&quot;],\n  &quot;policy_result&quot;: &quot;approval_required&quot;,\n  &quot;reason&quot;: &quot;Compensation fields may be used in aggregate analysis only with approved purpose and minimum group size checks.&quot;\n}\n<\/code><\/pre>\n<p>The goal is not always to block. The goal is to make the decision explicit.<\/p>\n<h2>Example 4: Derived Columns Can Still Reveal Restricted Fields<\/h2>\n<p>A generated query may avoid selecting the raw sensitive field but derive a new output from it:<\/p>\n<pre><code class=\"language-sql\">SELECT\n  customer_id,\n  CASE\n    WHEN credit_score &gt;= 720 THEN 'prime'\n    WHEN credit_score &gt;= 660 THEN 'near_prime'\n    ELSE 'subprime'\n  END AS credit_segment\nFROM finance.customer_risk;\n<\/code><\/pre>\n<p>The output column <code>credit_segment<\/code> is derived from <code>credit_score<\/code>. If <code>credit_score<\/code> is restricted, the derived output may also need a policy decision.<\/p>\n<p>This is where field-level permissions and column-level lineage meet. The system should understand:<\/p>\n<pre><code class=\"language-text\">query_result.credit_segment &lt;- finance.customer_risk.credit_score\n<\/code><\/pre>\n<p>The policy decision might be:<\/p>\n<pre><code class=\"language-json\">{\n  &quot;decision&quot;: &quot;approval_required&quot;,\n  &quot;reason&quot;: &quot;Output credit_segment is derived from restricted credit_score.&quot;,\n  &quot;lineage&quot;: [\n    {\n      &quot;target&quot;: &quot;query_result.credit_segment&quot;,\n      &quot;source&quot;: &quot;finance.customer_risk.credit_score&quot;,\n      &quot;usage&quot;: &quot;derived_expression&quot;,\n      &quot;labels&quot;: [&quot;financial_risk&quot;, &quot;regulated&quot;]\n    }\n  ]\n}\n<\/code><\/pre>\n<p>Without lineage, the system may treat <code>credit_segment<\/code> as a harmless new field. With lineage, it can carry the sensitivity of the source column into the derived output.<\/p>\n<h2>What the Policy Engine Needs as Input<\/h2>\n<p>A useful field-level permission check does not start from SQL alone. It needs a request envelope and governance metadata.<\/p>\n<p>A minimal request might include:<\/p>\n<pre><code class=\"language-json\">{\n  &quot;request_id&quot;: &quot;req_2026_05_field_001&quot;,\n  &quot;user&quot;: {\n    &quot;id&quot;: &quot;u_12345&quot;,\n    &quot;roles&quot;: [&quot;sales_analyst&quot;],\n    &quot;department&quot;: &quot;sales_operations&quot;\n  },\n  &quot;purpose&quot;: &quot;interactive_chatbi&quot;,\n  &quot;environment&quot;: &quot;production_readonly&quot;,\n  &quot;dialect&quot;: &quot;postgresql&quot;,\n  &quot;generated_sql&quot;: &quot;SELECT customer_id, email, total_spend FROM analytics.customers ORDER BY total_spend DESC LIMIT 50&quot;\n}\n<\/code><\/pre>\n<p>The catalog should describe tables and columns:<\/p>\n<pre><code class=\"language-yaml\">schemas:\n  analytics:\n    tables:\n      customers:\n        columns:\n          customer_id:\n            type: string\n          email:\n            type: string\n          phone:\n            type: string\n          total_spend:\n            type: decimal\n          state:\n            type: string\n<\/code><\/pre>\n<p>Classification metadata should describe sensitivity:<\/p>\n<pre><code class=\"language-yaml\">classifications:\n  analytics.customers.email:\n    labels: [PII, contact]\n    sensitivity: high\n\n  analytics.customers.phone:\n    labels: [PII, contact]\n    sensitivity: high\n\n  analytics.customers.total_spend:\n    labels: [financial_behavior]\n    sensitivity: medium\n<\/code><\/pre>\n<p>Policy rules should express decisions:<\/p>\n<pre><code class=\"language-yaml\">policies:\n  - id: deny_pii_projection_for_sales_analyst\n    type: field_access\n    effect: deny\n    when:\n      role: sales_analyst\n      field_labels_any: [PII]\n      usage_any: [projection]\n\n  - id: warn_financial_behavior_for_interactive_chatbi\n    type: field_access\n    effect: warn\n    when:\n      purpose: interactive_chatbi\n      field_labels_any: [financial_behavior]\n\n  - id: approval_for_sensitive_export\n    type: query_shape\n    effect: approval_required\n    when:\n      result_limit_greater_than: 1000\n      field_labels_any: [PII, financial_behavior]\n<\/code><\/pre>\n<p>These examples are intentionally simple. In a production environment, policies may come from IAM, data catalog labels, privacy systems, security review workflows, and business rules. But the core pattern is the same: SQL facts plus user context plus field classifications produce a policy decision.<\/p>\n<h2>Why String Matching Fails<\/h2>\n<p>Some teams try to block fields with simple text patterns:<\/p>\n<pre><code class=\"language-text\">if SQL contains &quot;email&quot;, block it\nif SQL contains &quot;ssn&quot;, block it\nif SQL contains &quot;salary&quot;, require approval\n<\/code><\/pre>\n<p>This approach breaks quickly.<\/p>\n<p>First, aliases can hide field names:<\/p>\n<pre><code class=\"language-sql\">SELECT c.email AS contact\nFROM customers c;\n<\/code><\/pre>\n<p>Second, unqualified names need binding:<\/p>\n<pre><code class=\"language-sql\">SELECT email\nFROM customers;\n<\/code><\/pre>\n<p>The system must know which <code>email<\/code> column this means.<\/p>\n<p>Third, CTEs and subqueries can rename fields:<\/p>\n<pre><code class=\"language-sql\">WITH contacts AS (\n  SELECT customer_id, email AS contact_key\n  FROM customers\n)\nSELECT contact_key\nFROM contacts;\n<\/code><\/pre>\n<p>Fourth, expressions can derive sensitive outputs:<\/p>\n<pre><code class=\"language-sql\">SELECT SHA256(email) AS email_hash\nFROM customers;\n<\/code><\/pre>\n<p>Hashing may reduce exposure in some contexts, but it does not automatically remove governance risk. The output still depends on a PII source field, and policy should decide whether the transformation is acceptable.<\/p>\n<p>Fifth, different dialects have different quoting, struct access, JSON operators, and function behavior. A reliable checker needs a dialect-aware parser and a semantic binding layer.<\/p>\n<h2>A Practical Evaluation Flow<\/h2>\n<p>A production Text-to-SQL system should place field-level permission checks after SQL generation and before execution:<\/p>\n<pre><code class=\"language-text\">User question\n  \u2193\nLLM generates SQL\n  \u2193\nSQL parser\n  \u2193\nCatalog binding\n  \u2193\nField usage extraction\n  \u2193\nSensitive-field classification\n  \u2193\nPolicy evaluation\n  \u2193\nallow \/ warn \/ deny \/ approval_required\n  \u2193\nExecute, repair, approve, or reject\n  \u2193\nAudit log\n<\/code><\/pre>\n<p>The important point is that the field check is not a separate static checklist. It depends on the generated query. Two prompts from the same user can produce different SQL shapes and therefore different policy outcomes.<\/p>\n<h2>Example SQL Facts JSON<\/h2>\n<p>For a generated query:<\/p>\n<pre><code class=\"language-sql\">SELECT\n  customer_id,\n  email,\n  total_spend\nFROM analytics.customers\nWHERE state = 'CA'\nORDER BY total_spend DESC\nLIMIT 50;\n<\/code><\/pre>\n<p>A useful SQL facts output might look like this:<\/p>\n<pre><code class=\"language-json\">{\n  &quot;sql_id&quot;: &quot;chatbi_042&quot;,\n  &quot;dialect&quot;: &quot;postgresql&quot;,\n  &quot;parse_status&quot;: &quot;success&quot;,\n  &quot;statement_type&quot;: &quot;select&quot;,\n  &quot;tables&quot;: [\n    {\n      &quot;name&quot;: &quot;customers&quot;,\n      &quot;schema&quot;: &quot;analytics&quot;,\n      &quot;alias&quot;: null\n    }\n  ],\n  &quot;field_usage&quot;: [\n    {\n      &quot;field&quot;: &quot;analytics.customers.customer_id&quot;,\n      &quot;usage&quot;: &quot;projection&quot;,\n      &quot;labels&quot;: [],\n      &quot;sensitivity&quot;: &quot;low&quot;\n    },\n    {\n      &quot;field&quot;: &quot;analytics.customers.email&quot;,\n      &quot;usage&quot;: &quot;projection&quot;,\n      &quot;labels&quot;: [&quot;PII&quot;, &quot;contact&quot;],\n      &quot;sensitivity&quot;: &quot;high&quot;\n    },\n    {\n      &quot;field&quot;: &quot;analytics.customers.total_spend&quot;,\n      &quot;usage&quot;: &quot;projection&quot;,\n      &quot;labels&quot;: [&quot;financial_behavior&quot;],\n      &quot;sensitivity&quot;: &quot;medium&quot;\n    },\n    {\n      &quot;field&quot;: &quot;analytics.customers.total_spend&quot;,\n      &quot;usage&quot;: &quot;ordering&quot;,\n      &quot;labels&quot;: [&quot;financial_behavior&quot;],\n      &quot;sensitivity&quot;: &quot;medium&quot;\n    },\n    {\n      &quot;field&quot;: &quot;analytics.customers.state&quot;,\n      &quot;usage&quot;: &quot;filter&quot;,\n      &quot;labels&quot;: [&quot;location&quot;],\n      &quot;sensitivity&quot;: &quot;low&quot;\n    }\n  ],\n  &quot;policy_matches&quot;: [\n    {\n      &quot;policy_id&quot;: &quot;deny_pii_projection_for_sales_analyst&quot;,\n      &quot;effect&quot;: &quot;deny&quot;,\n      &quot;field&quot;: &quot;analytics.customers.email&quot;,\n      &quot;reason&quot;: &quot;Sales analyst cannot project PII contact fields.&quot;\n    },\n    {\n      &quot;policy_id&quot;: &quot;warn_financial_behavior_for_interactive_chatbi&quot;,\n      &quot;effect&quot;: &quot;warn&quot;,\n      &quot;field&quot;: &quot;analytics.customers.total_spend&quot;,\n      &quot;reason&quot;: &quot;Financial behavior field used in interactive ChatBI query.&quot;\n    }\n  ],\n  &quot;decision&quot;: &quot;deny&quot;,\n  &quot;recommended_action&quot;: &quot;Remove email from the projection, or request approval under a role allowed to access contact PII.&quot;\n}\n<\/code><\/pre>\n<p>This structure is useful because it can serve multiple audiences:<\/p>\n<ul>\n<li>the application can block or repair the query;<\/li>\n<li>the user can receive a clear explanation;<\/li>\n<li>the security team can review policy matches;<\/li>\n<li>the governance team can inspect sensitive-field usage patterns;<\/li>\n<li>the engineering team can build stable APIs around SQL facts.<\/li>\n<\/ul>\n<h2>Decision Model: Allow, Warn, Deny, Approval Required<\/h2>\n<p>A binary allow\/deny model is often too rigid for enterprise Text-to-SQL. Some queries are safe. Some are clearly prohibited. Others should be allowed with a warning, masked output, row limit, or approval workflow.<\/p>\n<table>\n<thead>\n<tr>\n<th>Decision<\/th>\n<th>When to use<\/th>\n<th>Example<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td><code>allow<\/code><\/td>\n<td>No relevant policy violation<\/td>\n<td>Non-sensitive aggregate by region<\/td>\n<\/tr>\n<tr>\n<td><code>warn<\/code><\/td>\n<td>Query is allowed but should be visible to the user or reviewer<\/td>\n<td>Medium-sensitivity field used in aggregation<\/td>\n<\/tr>\n<tr>\n<td><code>deny<\/code><\/td>\n<td>Query violates a hard rule<\/td>\n<td>Analyst selects raw email or SSN<\/td>\n<\/tr>\n<tr>\n<td><code>approval_required<\/code><\/td>\n<td>Query may be legitimate but needs human review<\/td>\n<td>Aggregate compensation query for HR planning<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>For generated SQL, these decisions should be returned before execution. The database should not be the first place where permission problems appear.<\/p>\n<h2>How This Connects to SQL Semantic Validation<\/h2>\n<p>Field-level permission checks depend on SQL semantic validation.<\/p>\n<p>A policy engine cannot reliably evaluate <code>customers.email<\/code> unless the system can first answer:<\/p>\n<ul>\n<li>Does <code>customers<\/code> refer to the expected table?<\/li>\n<li>Does <code>email<\/code> exist in that table?<\/li>\n<li>Is <code>email<\/code> an output column, filter dependency, join key, aggregation input, or derived source?<\/li>\n<li>Did a CTE rename it?<\/li>\n<li>Did <code>SELECT *<\/code> expand to include it?<\/li>\n<li>Is there an alias hiding the original field?<\/li>\n<\/ul>\n<p>This is why field-level permission checking should not be implemented as a string filter around an LLM. It needs a catalog-aware SQL semantic layer.<\/p>\n<h2>How This Connects to Column-Level Lineage<\/h2>\n<p>Column-level lineage explains how output columns depend on source columns. That is essential when sensitive data moves through transformations.<\/p>\n<p>For example:<\/p>\n<pre><code class=\"language-sql\">SELECT\n  customer_id,\n  SHA256(email) AS email_hash\nFROM analytics.customers;\n<\/code><\/pre>\n<p>A lineage-aware system should know:<\/p>\n<pre><code class=\"language-text\">query_result.email_hash &lt;- analytics.customers.email\n<\/code><\/pre>\n<p>Then policy can decide whether hashing is sufficient for the current role and purpose. In some environments, hashed email may be allowed for matching workflows. In others, it may still be restricted because it can be joined back to identity data.<\/p>\n<p>This is why field-level permission, sensitive-field detection, and lineage should not be treated as separate silos. They are different views of the same SQL facts.<\/p>\n<h2>What to Log for Audit<\/h2>\n<p>A field-level decision should be reviewable later. A useful audit event should include:<\/p>\n<ul>\n<li>request ID;<\/li>\n<li>user or role context;<\/li>\n<li>purpose and environment;<\/li>\n<li>generated SQL hash or stored SQL, depending on policy;<\/li>\n<li>dialect;<\/li>\n<li>tables and fields referenced;<\/li>\n<li>sensitive labels;<\/li>\n<li>field usage roles;<\/li>\n<li>matched policies;<\/li>\n<li>final decision;<\/li>\n<li>recommended action;<\/li>\n<li>whether the query was executed, repaired, approved, or rejected.<\/li>\n<\/ul>\n<p>A simplified audit record might look like this:<\/p>\n<pre><code class=\"language-json\">{\n  &quot;event_type&quot;: &quot;text_to_sql_policy_decision&quot;,\n  &quot;request_id&quot;: &quot;req_2026_05_field_001&quot;,\n  &quot;user_role&quot;: &quot;sales_analyst&quot;,\n  &quot;purpose&quot;: &quot;interactive_chatbi&quot;,\n  &quot;decision&quot;: &quot;deny&quot;,\n  &quot;matched_policies&quot;: [&quot;deny_pii_projection_for_sales_analyst&quot;],\n  &quot;sensitive_fields&quot;: [\n    {\n      &quot;field&quot;: &quot;analytics.customers.email&quot;,\n      &quot;usage&quot;: &quot;projection&quot;,\n      &quot;labels&quot;: [&quot;PII&quot;, &quot;contact&quot;]\n    }\n  ],\n  &quot;action&quot;: &quot;query_rejected_before_execution&quot;\n}\n<\/code><\/pre>\n<p>This audit trail matters because enterprise AI systems need more than a final answer. They need evidence of control.<\/p>\n<h2>Practical Checklist for Teams<\/h2>\n<p>Use this checklist when evaluating a Text-to-SQL or ChatBI system:<\/p>\n<ul>\n<li>Can the system parse generated SQL for your actual dialects?<\/li>\n<li>Can it bind unqualified columns, aliases, CTEs, subqueries, and <code>SELECT *<\/code> to catalog metadata?<\/li>\n<li>Can it identify sensitive fields in projections, filters, joins, aggregations, grouping, ordering, and derived outputs?<\/li>\n<li>Can it distinguish direct exposure from aggregate use?<\/li>\n<li>Can it carry sensitivity through derived columns using lineage?<\/li>\n<li>Can policies use user role, purpose, environment, field labels, and usage role?<\/li>\n<li>Can it return <code>allow<\/code>, <code>warn<\/code>, <code>deny<\/code>, and <code>approval_required<\/code>?<\/li>\n<li>Can it explain which field and policy caused the decision?<\/li>\n<li>Can it fail safely when SQL cannot be parsed or bound?<\/li>\n<li>Can it write an audit record before execution?<\/li>\n<li>Can reviewers test the system with 50\u2013100 representative SQL samples?<\/li>\n<\/ul>\n<p>If the answer is no to several of these questions, the Text-to-SQL workflow may still be useful for demos, but it is not yet ready for governed production use.<\/p>\n<h2>Common Questions<\/h2>\n<h3>Are database permissions enough for Text-to-SQL?<\/h3>\n<p>Database permissions are necessary, but they are usually not enough by themselves. A database can enforce grants at execution time, but a Text-to-SQL governance layer should inspect the generated SQL before execution, with user intent, application role, purpose, field labels, policy rules, and audit requirements.<\/p>\n<h3>What is the difference between table-level and field-level permission?<\/h3>\n<p>Table-level permission decides whether a user can access a table. Field-level permission decides whether a user can access or use specific columns within that table. A user may be allowed to query <code>customers<\/code> for aggregate analytics but not allowed to select <code>customers.email<\/code> or filter by <code>customers.ssn_last4<\/code>.<\/p>\n<h3>Should field-level checks inspect only the SELECT list?<\/h3>\n<p>No. Field usage can appear in <code>SELECT<\/code>, <code>WHERE<\/code>, <code>JOIN<\/code>, <code>GROUP BY<\/code>, <code>HAVING<\/code>, <code>ORDER BY<\/code>, window functions, CTEs, subqueries, and derived expressions. Sensitive access can affect results even when the sensitive field is not displayed.<\/p>\n<h3>Can an LLM judge whether a field is sensitive?<\/h3>\n<p>An LLM can help explain policy messages, but the core permission decision should not depend on the model guessing. Sensitive-field detection should use approved metadata such as catalog labels, glossary terms, classification rules, and policy configuration.<\/p>\n<h3>How does field-level permission relate to SQL lineage?<\/h3>\n<p>Lineage shows which source fields contribute to output fields. That matters when sensitive fields are transformed or renamed. If <code>email_hash<\/code> is derived from <code>customers.email<\/code>, policy may still need to treat the output as sensitive or restricted.<\/p>\n<h3>What should happen when the policy engine is uncertain?<\/h3>\n<p>A governed system should fail safely. Depending on the risk, it can return <code>deny<\/code>, <code>approval_required<\/code>, or <code>repair<\/code> with a clear reason. It should not silently allow SQL it cannot parse, bind, or classify.<\/p>\n<h2>Summary Table<\/h2>\n<table>\n<thead>\n<tr>\n<th>Topic<\/th>\n<th>Practical answer<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>Core problem<\/td>\n<td>Text-to-SQL systems generate SQL dynamically, so permissions must be checked against the actual generated query.<\/td>\n<\/tr>\n<tr>\n<td>Why table access is insufficient<\/td>\n<td>An allowed table can contain restricted fields such as PII, financial data, HR data, credentials, or regulated attributes.<\/td>\n<\/tr>\n<tr>\n<td>Required analysis<\/td>\n<td>SQL parsing, catalog binding, field usage extraction, sensitive-field classification, policy evaluation, lineage, and audit logging.<\/td>\n<\/tr>\n<tr>\n<td>Hard cases<\/td>\n<td><code>SELECT *<\/code>, aliases, CTEs, filters, joins, aggregations, derived columns, hashes, window functions, and dialect-specific syntax.<\/td>\n<\/tr>\n<tr>\n<td>Decision model<\/td>\n<td><code>allow<\/code>, <code>warn<\/code>, <code>deny<\/code>, or <code>approval_required<\/code>.<\/td>\n<\/tr>\n<tr>\n<td>Governance value<\/td>\n<td>Prevents unauthorized field use before execution and creates reviewable audit evidence.<\/td>\n<\/tr>\n<tr>\n<td>Evaluation approach<\/td>\n<td>Test with representative generated SQL, catalog metadata, field classifications, roles, and policies.<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h2>Conclusion<\/h2>\n<p>Field-level permission checks are one of the most important controls for production Text-to-SQL. They close the gap between \u201cthis user can query a table\u201d and \u201cthis generated SQL is allowed to use these specific fields in this specific way.\u201d<\/p>\n<p>A practical implementation needs more than prompts, string matching, or table grants. It needs SQL parsing, catalog binding, sensitive-field metadata, usage-aware policy rules, lineage for derived outputs, explicit decisions, and audit logs.<\/p>\n<p>For teams building ChatBI, Text-to-SQL, or AI data agents, this capability is not a nice-to-have. It is part of the control layer that determines whether generated SQL can be safely executed, repaired, approved, or rejected before it reaches the database.<\/p>\n<p>If you want to evaluate a single generated query first, you can <a href=\"https:\/\/www.dpriver.com\/pp\/sqlformat.htm?utm_source=dpriver_blog&amp;utm_medium=blog_cta&amp;utm_campaign=llm_sql_guard&amp;utm_content=sqlguard_test\">try SQL Guard-style validation with your SQL<\/a>.<\/p>\n<p>For a broader review, collect 50\u2013100 representative generated SQL queries, include role and field-classification context, and use the results to assess whether your Text-to-SQL workflow is ready for governed production use.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Table-level permissions are not enough for Text-to-SQL. This guide explains how field-level permission checks detect sensitive columns and enforce policy before generated SQL reaches the database.<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":[],"categories":[170,172,175],"tags":[169,186,184,164,185,181,165],"blocksy_meta":{"styles_descriptor":{"styles":{"desktop":"","tablet":"","mobile":""},"google_fonts":[],"version":5}},"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v19.4 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Field-Level Permission Checks for Text-to-SQL Systems<\/title>\n<meta name=\"description\" content=\"Field-Level Permission Checks for Text-to-SQL Systems\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.dpriver.com\/blog\/field-level-permission-checks-for-text-to-sql-systems\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Field-Level Permission Checks for Text-to-SQL Systems\" \/>\n<meta property=\"og:description\" content=\"Field-Level Permission Checks for Text-to-SQL Systems\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.dpriver.com\/blog\/field-level-permission-checks-for-text-to-sql-systems\/\" \/>\n<meta property=\"og:site_name\" content=\"SQL and Data Blog\" \/>\n<meta property=\"article:published_time\" content=\"2026-05-05T09:54:03+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2026-05-05T10:16:45+00:00\" \/>\n<meta name=\"author\" content=\"James\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"James\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"19 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Organization\",\"@id\":\"https:\/\/www.dpriver.com\/blog\/#organization\",\"name\":\"SQL and Data Blog\",\"url\":\"https:\/\/www.dpriver.com\/blog\/\",\"sameAs\":[],\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.dpriver.com\/blog\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/www.dpriver.com\/blog\/wp-content\/uploads\/2022\/07\/sqlpp-character.png\",\"contentUrl\":\"https:\/\/www.dpriver.com\/blog\/wp-content\/uploads\/2022\/07\/sqlpp-character.png\",\"width\":251,\"height\":72,\"caption\":\"SQL and Data Blog\"},\"image\":{\"@id\":\"https:\/\/www.dpriver.com\/blog\/#\/schema\/logo\/image\/\"}},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/www.dpriver.com\/blog\/#website\",\"url\":\"https:\/\/www.dpriver.com\/blog\/\",\"name\":\"SQL and Data Blog\",\"description\":\"SQL related blog for database professional\",\"publisher\":{\"@id\":\"https:\/\/www.dpriver.com\/blog\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/www.dpriver.com\/blog\/?s={search_term_string}\"},\"query-input\":\"required name=search_term_string\"}],\"inLanguage\":\"en-US\"},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/www.dpriver.com\/blog\/field-level-permission-checks-for-text-to-sql-systems\/\",\"url\":\"https:\/\/www.dpriver.com\/blog\/field-level-permission-checks-for-text-to-sql-systems\/\",\"name\":\"Field-Level Permission Checks for Text-to-SQL Systems\",\"isPartOf\":{\"@id\":\"https:\/\/www.dpriver.com\/blog\/#website\"},\"datePublished\":\"2026-05-05T09:54:03+00:00\",\"dateModified\":\"2026-05-05T10:16:45+00:00\",\"description\":\"Field-Level Permission Checks for Text-to-SQL Systems\",\"breadcrumb\":{\"@id\":\"https:\/\/www.dpriver.com\/blog\/field-level-permission-checks-for-text-to-sql-systems\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/www.dpriver.com\/blog\/field-level-permission-checks-for-text-to-sql-systems\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/www.dpriver.com\/blog\/field-level-permission-checks-for-text-to-sql-systems\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/www.dpriver.com\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Field-Level Permission Checks for Text-to-SQL Systems\"}]},{\"@type\":\"Article\",\"@id\":\"https:\/\/www.dpriver.com\/blog\/field-level-permission-checks-for-text-to-sql-systems\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/www.dpriver.com\/blog\/field-level-permission-checks-for-text-to-sql-systems\/\"},\"author\":{\"name\":\"James\",\"@id\":\"https:\/\/www.dpriver.com\/blog\/#\/schema\/person\/7bbdbb6e79c5dd9747d08c59d5992b04\"},\"headline\":\"Field-Level Permission Checks for Text-to-SQL Systems\",\"datePublished\":\"2026-05-05T09:54:03+00:00\",\"dateModified\":\"2026-05-05T10:16:45+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/www.dpriver.com\/blog\/field-level-permission-checks-for-text-to-sql-systems\/\"},\"wordCount\":2415,\"publisher\":{\"@id\":\"https:\/\/www.dpriver.com\/blog\/#organization\"},\"keywords\":[\"ai-data-governance\",\"column-level-access-control\",\"field-level-permissions\",\"llm-sql-guard\",\"sensitive-field-detection\",\"sql-policy-engine\",\"text-to-sql-security\"],\"articleSection\":[\"AI Data Governance\",\"Data Lineage\",\"SQL Security\"],\"inLanguage\":\"en-US\"},{\"@type\":\"Person\",\"@id\":\"https:\/\/www.dpriver.com\/blog\/#\/schema\/person\/7bbdbb6e79c5dd9747d08c59d5992b04\",\"name\":\"James\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.dpriver.com\/blog\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/eeddf4ca7bdafa37ab025068efdc7302?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/eeddf4ca7bdafa37ab025068efdc7302?s=96&d=mm&r=g\",\"caption\":\"James\"},\"sameAs\":[\"http:\/\/www.dpriver.com\"],\"url\":\"https:\/\/www.dpriver.com\/blog\/author\/james\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Field-Level Permission Checks for Text-to-SQL Systems","description":"Field-Level Permission Checks for Text-to-SQL Systems","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.dpriver.com\/blog\/field-level-permission-checks-for-text-to-sql-systems\/","og_locale":"en_US","og_type":"article","og_title":"Field-Level Permission Checks for Text-to-SQL Systems","og_description":"Field-Level Permission Checks for Text-to-SQL Systems","og_url":"https:\/\/www.dpriver.com\/blog\/field-level-permission-checks-for-text-to-sql-systems\/","og_site_name":"SQL and Data Blog","article_published_time":"2026-05-05T09:54:03+00:00","article_modified_time":"2026-05-05T10:16:45+00:00","author":"James","twitter_card":"summary_large_image","twitter_misc":{"Written by":"James","Est. reading time":"19 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Organization","@id":"https:\/\/www.dpriver.com\/blog\/#organization","name":"SQL and Data Blog","url":"https:\/\/www.dpriver.com\/blog\/","sameAs":[],"logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.dpriver.com\/blog\/#\/schema\/logo\/image\/","url":"https:\/\/www.dpriver.com\/blog\/wp-content\/uploads\/2022\/07\/sqlpp-character.png","contentUrl":"https:\/\/www.dpriver.com\/blog\/wp-content\/uploads\/2022\/07\/sqlpp-character.png","width":251,"height":72,"caption":"SQL and Data Blog"},"image":{"@id":"https:\/\/www.dpriver.com\/blog\/#\/schema\/logo\/image\/"}},{"@type":"WebSite","@id":"https:\/\/www.dpriver.com\/blog\/#website","url":"https:\/\/www.dpriver.com\/blog\/","name":"SQL and Data Blog","description":"SQL related blog for database professional","publisher":{"@id":"https:\/\/www.dpriver.com\/blog\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.dpriver.com\/blog\/?s={search_term_string}"},"query-input":"required name=search_term_string"}],"inLanguage":"en-US"},{"@type":"WebPage","@id":"https:\/\/www.dpriver.com\/blog\/field-level-permission-checks-for-text-to-sql-systems\/","url":"https:\/\/www.dpriver.com\/blog\/field-level-permission-checks-for-text-to-sql-systems\/","name":"Field-Level Permission Checks for Text-to-SQL Systems","isPartOf":{"@id":"https:\/\/www.dpriver.com\/blog\/#website"},"datePublished":"2026-05-05T09:54:03+00:00","dateModified":"2026-05-05T10:16:45+00:00","description":"Field-Level Permission Checks for Text-to-SQL Systems","breadcrumb":{"@id":"https:\/\/www.dpriver.com\/blog\/field-level-permission-checks-for-text-to-sql-systems\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.dpriver.com\/blog\/field-level-permission-checks-for-text-to-sql-systems\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/www.dpriver.com\/blog\/field-level-permission-checks-for-text-to-sql-systems\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.dpriver.com\/blog\/"},{"@type":"ListItem","position":2,"name":"Field-Level Permission Checks for Text-to-SQL Systems"}]},{"@type":"Article","@id":"https:\/\/www.dpriver.com\/blog\/field-level-permission-checks-for-text-to-sql-systems\/#article","isPartOf":{"@id":"https:\/\/www.dpriver.com\/blog\/field-level-permission-checks-for-text-to-sql-systems\/"},"author":{"name":"James","@id":"https:\/\/www.dpriver.com\/blog\/#\/schema\/person\/7bbdbb6e79c5dd9747d08c59d5992b04"},"headline":"Field-Level Permission Checks for Text-to-SQL Systems","datePublished":"2026-05-05T09:54:03+00:00","dateModified":"2026-05-05T10:16:45+00:00","mainEntityOfPage":{"@id":"https:\/\/www.dpriver.com\/blog\/field-level-permission-checks-for-text-to-sql-systems\/"},"wordCount":2415,"publisher":{"@id":"https:\/\/www.dpriver.com\/blog\/#organization"},"keywords":["ai-data-governance","column-level-access-control","field-level-permissions","llm-sql-guard","sensitive-field-detection","sql-policy-engine","text-to-sql-security"],"articleSection":["AI Data Governance","Data Lineage","SQL Security"],"inLanguage":"en-US"},{"@type":"Person","@id":"https:\/\/www.dpriver.com\/blog\/#\/schema\/person\/7bbdbb6e79c5dd9747d08c59d5992b04","name":"James","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.dpriver.com\/blog\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/eeddf4ca7bdafa37ab025068efdc7302?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/eeddf4ca7bdafa37ab025068efdc7302?s=96&d=mm&r=g","caption":"James"},"sameAs":["http:\/\/www.dpriver.com"],"url":"https:\/\/www.dpriver.com\/blog\/author\/james\/"}]}},"_links":{"self":[{"href":"https:\/\/www.dpriver.com\/blog\/wp-json\/wp\/v2\/posts\/3244"}],"collection":[{"href":"https:\/\/www.dpriver.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.dpriver.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.dpriver.com\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/www.dpriver.com\/blog\/wp-json\/wp\/v2\/comments?post=3244"}],"version-history":[{"count":1,"href":"https:\/\/www.dpriver.com\/blog\/wp-json\/wp\/v2\/posts\/3244\/revisions"}],"predecessor-version":[{"id":3246,"href":"https:\/\/www.dpriver.com\/blog\/wp-json\/wp\/v2\/posts\/3244\/revisions\/3246"}],"wp:attachment":[{"href":"https:\/\/www.dpriver.com\/blog\/wp-json\/wp\/v2\/media?parent=3244"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.dpriver.com\/blog\/wp-json\/wp\/v2\/categories?post=3244"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.dpriver.com\/blog\/wp-json\/wp\/v2\/tags?post=3244"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}