Security advisory GHSA-7q94-qpjr-xpgm: langchain SQL Injection vulnerability
Package: langchain (pypi), vulnerable version: 0.0.246, fixed in: 0.0.247
CWE: CWE-89
Files changed by the fix:
langchain/chains/sql_database/base.py

Fix diff:
diff --git a/langchain/chains/sql_database/base.py b/langchain/chains/sql_database/base.py
index 843a041bc0582..7d3324a86f674 100644
--- a/langchain/chains/sql_database/base.py
+++ b/langchain/chains/sql_database/base.py
@@ -2,8 +2,9 @@
 from __future__ import annotations
 
 import warnings
-from typing import Any, Dict, List, Optional
+from typing import Any, Dict, Iterator, List, Optional, Union
 
+import sqlfluff
 from pydantic import Extra, Field, root_validator
 
 from langchain.base_language import BaseLanguageModel
@@ -19,6 +20,106 @@
 INTERMEDIATE_STEPS_KEY = "intermediate_steps"
 
 
+def get_json_segment(
+    parse_result: Dict[str, Any], segment_type: str
+) -> Iterator[Union[str, Dict[str, Any], List[Dict[str, Any]]]]:
+    """Recursively search JSON parse result for specified segment type.
+
+    Args:
+        parse_result (Dict[str, Any]): JSON parse result from `sqlfluff.fix`.
+        segment_type (str): The segment type to search for.
+
+    Yields:
+        Iterator[Union[str, Dict[str, Any], List[Dict[str, Any]]]]:
+        Retrieves children of specified segment type as either a string for a raw
+        segment or as JSON or an array of JSON for non-raw segments.
+    """
+    for k, v in parse_result.items():
+        if k == segment_type:
+            yield v
+        elif isinstance(v, dict):
+            yield from get_json_segment(v, segment_type)
+        elif isinstance(v, list):
+            for s in v:
+                yield from get_json_segment(s, segment_type)
+
+
+def validate_sql(sql_cmd: str, dialect: str, sql_validation: SQLValidation) -> None:
+    """Parse an SQL query from a given dialect and determine if it passes validations.
+
+    Args:
+        sql_cmd (str): SQL query to validate.
+        dialect (str): Dialect of the SQL query.
+        sql_validation (SQLValidation):
+            Determines which validations need to be performed
+
+    """
+    try:
+        parse_result = sqlfluff.parse(sql=sql_cmd, dialect=dialect)
+    except sqlfluff.api.simple.APIParsingError as e:
+        raise ValueError(f"Parsing of SQL query `{sql_cmd}` failed: {e.msg}")
+    except sqlfluff.core.errors.SQLFluffUserError:
+        unsupported_dialect_message = (
+            f"Dialect {dialect} unsupported for "
+            "SQL validation. No validation will be done. "
+            "Go to https://docs.sqlfluff.com/en/stable/dialects.html "
+            "to see supported dialects"
+        )
+        if sql_validation.allow_unsupported_dialect is False:
+            raise ValueError(unsupported_dialect_message)
+        warnings.warn(unsupported_dialect_message)
+        return
+
+    if sql_validation.allow_non_select_statements is False:
+        statements = get_json_segment(parse_result, "statement")
+        for statement in statements:
+            if (
+                hasattr(statement, "keys")
+                and list(statement.keys())[0] != "select_statement"
+            ):
+                raise ValueError(
+                    f"Found disallowed non select statement `{statement}`"
+                    f"in SQL query `{sql_cmd}`"
+                )
+
+    if sql_validation.allow_select_all_statements is False:
+        select_statements = get_json_segment(parse_result, "select_statement")
+        for select_statement in select_statements:
+            if not isinstance(select_statement, dict):
+                continue
+            wildcard_expressions = get_json_segment(
+                select_statement, "wildcard_expression"
+            )
+            found_wildcard_expressions = list(wildcard_expressions)
+            if len(found_wildcard_expressions) > 0:
+                raise ValueError(
+                    "Found disallowed wildcard select statement(s) "
+                    f"`{found_wildcard_expressions}` in query `{sql_cmd}`"
+                )
+
+
+class SQLValidation(object):
+    def __init__(
+        self,
+        allow_unsupported_dialect: bool = True,
+        allow_non_select_statements: bool = False,
+        allow_select_all_statements: bool = True,
+    ):
+        """Initialize an SQLValidation instance
+
+        Args:
+            allow_unsupported_dialect (bool): If unsupported dialects are allowed,
+                no validations will be done on them.
+            allow_non_select_statements (bool):
+                Allow statments that are non select ones, such as DROP.
+            allow_select_all_statements (bool):
+                Allow statments that are selecting all columns (SELECT *).
+        """
+        self.allow_unsupported_dialect = allow_unsupported_dialect
+        self.allow_non_select_statements = allow_non_select_statements
+        self.allow_select_all_statements = allow_select_all_statements
+
+
 class SQLDatabaseChain(Chain):
     """Chain for interacting with SQL Database.
 
@@ -50,6 +151,7 @@ class SQLDatabaseChain(Chain):
     to fix the initial SQL from the LLM."""
     query_checker_prompt: Optional[BasePromptTemplate] = None
     """The prompt template that should be used by the query checker"""
+    sql_validation: SQLValidation = SQLValidation()
 
     class Config:
         """Configuration for this pydantic object."""
@@ -117,6 +219,9 @@ def _call(
                 callbacks=_run_manager.get_child(),
                 **llm_inputs,
             ).strip()
+
+            validate_sql(sql_cmd, self.database.dialect, self.sql_validation)
+
             if not self.use_query_checker:
                 _run_manager.on_text(sql_cmd, color="green", verbose=self.verbose)
                 intermediate_steps.append(
diff --git a/poetry.lock b/poetry.lock
index 8de0b4ce44566..56700cc7f2691 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -330,6 +330,18 @@ doc = ["Sphinx (>=6.1.0)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "s
 test = ["anyio[trio]", "coverage[toml] (>=4.5)", "hypothesis (>=4.0)", "mock (>=4)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (>=0.17)"]
 trio = ["trio (<0.22)"]
 
+[[package]]
+name = "appdirs"
+version = "1.4.4"
+description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"."
+category = "main"
+optional = true
+python-versions = "*"
+files = [
+    {file = "appdirs-1.4.4-py2.py3-none-any.whl", hash = "sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128"},
+    {file = "appdirs-1.4.4.tar.gz", hash = "sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41"},
+]
+
 [[package]]
 name = "appnope"
 version = "0.1.3"
@@ -1923,6 +1935,27 @@ wrapt = ">=1.10,<2"
 [package.extras]
 dev = ["PyTest", "PyTest-Cov", "bump2version (<1)", "sphinx (<2)", "tox"]
 
+[[package]]
+name = "diff-cover"
+version = "7.6.0"
+description = "Run coverage and linting reports on diffs"
+category = "main"
+optional = true
+python-versions = ">=3.7.2,<4.0.0"
+files = [
+    {file = "diff_cover-7.6.0-py3-none-any.whl", hash = "sha256:e6e44554d6adb053f4cb292222e4cb772ef7d5a5690cdb51fe806f51f45dd690"},
+    {file = "diff_cover-7.6.0.tar.gz", hash = "sha256:d1007303f6bfba14925f602fc735ca8d6b254d8b57320f2b4b38a3a618453f40"},
+]
+
+[package.dependencies]
+chardet = ">=3.0.0"
+Jinja2 = ">=2.7.1"
+pluggy = ">=0.13.1,<2"
+Pygments = ">=2.9.0,<3.0.0"
+
+[package.extras]
+toml = ["tomli (>=1.2.1)"]
+
 [[package]]
 name = "dill"
 version = "0.3.6"
@@ -3384,7 +3417,7 @@ files = [
 name = "iniconfig"
 version = "2.0.0"
 description = "brain-dead simple config-ini parsing"
-category = "dev"
+category = "main"
 optional = false
 python-versions = ">=3.7"
 files = [
@@ -6639,7 +6672,7 @@ typing-extensions = {version = "*", markers = "python_version <= \"3.8\""}
 name = "pluggy"
 version = "1.0.0"
 description = "plugin and hook calling mechanisms for python"
-category = "dev"
+category = "main"
 optional = false
 python-versions = ">=3.6"
 files = [
@@ -7728,7 +7761,7 @@ Pillow = ">=8.0.0"
 name = "pytest"
 version = "7.3.2"
 description = "pytest: simple powerful testing with Python"
-category = "dev"
+category = "main"
 optional = false
 python-versions = ">=3.7"
 files = [
@@ -9449,6 +9482,34 @@ postgresql-psycopgbinary = ["psycopg[binary] (>=3.0.7)"]
 pymysql = ["pymysql"]
 sqlcipher = ["sqlcipher3-binary"]
 
+[[package]]
+name = "sqlfluff"
+version = "2.1.1"
+description = "The SQL Linter for Humans"
+category = "main"
+optional = true
+python-versions = ">=3.7"
+files = [
+    {file = "sqlfluff-2.1.1-py3-none-any.whl", hash = "sha256:7f303189ba3d1e50df5782c0bea974de8a25db0e6d1f0d1238f75b848c8d3f27"},
+    {file = "sqlfluff-2.1.1.tar.gz", hash = "sha256:974a2eecb91ab3236b73097ea6c7532a96dd0dcafd4394fa2305b2845ef4d7d2"},
+]
+
+[package.dependencies]
+appdirs = "*"
+chardet = "*"
+click = "*"
+colorama = ">=0.3"
+diff-cover = ">=2.5.0"
+Jinja2 = "*"
+pathspec = "*"
+pytest = "*"
+pyyaml = ">=5.1"
+regex = "*"
+tblib = "*"
+toml = {version = "*", markers = "python_version < \"3.11\""}
+tqdm = "*"
+typing-extensions = "*"
+
 [[package]]
 name = "sqlitedict"
 version = "2.1.0"
@@ -9647,6 +9708,18 @@ files = [
 [package.dependencies]
 redis = ">=4.4.4"
 
+[[package]]
+name = "tblib"
+version = "1.7.0"
+description = "Traceback serialization library."
+category = "main"
+optional = true
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
+files = [
+    {file = "tblib-1.7.0-py2.py3-none-any.whl", hash = "sha256:289fa7359e580950e7d9743eab36b0691f0310fce64dee7d9c31065b8f723e23"},
+    {file = "tblib-1.7.0.tar.gz", hash = "sha256:059bd77306ea7b419d4f76016aef6d7027cc8a0785579b5aad198803435f882c"},
+]
+
 [[package]]
 name = "telethon"
 version = "1.28.5"
@@ -11478,7 +11551,7 @@ azure = ["azure-ai-formrecognizer", "azure-ai-vision", "azure-cognitiveservices-
 cohere = ["cohere"]
 docarray = ["docarray"]
 embeddings = ["sentence-transformers"]
-extended-testing = ["atlassian-python-api", "beautifulsoup4", "beautifulsoup4", "bibtexparser", "chardet", "gql", "html2text", "jq", "lxml", "openai", "pandas", "pdfminer-six", "pgvector", "psychicapi", "py-trello", "pymupdf", "pypdf", "pypdfium2", "pyspark", "requests-toolbelt", "scikit-learn", "telethon", "tqdm", "zep-python"]
+extended-testing = ["atlassian-python-api", "beautifulsoup4", "beautifulsoup4", "bibtexparser", "chardet", "gql", "html2text", "jq", "lxml", "openai", "pandas", "pdfminer-six", "pgvector", "psychicapi", "py-trello", "pymupdf", "pypdf", "pypdfium2", "pyspark", "requests-toolbelt", "scikit-learn", "sqlfluff", "telethon", "tqdm", "zep-python"]
 llms = ["anthropic", "cohere", "huggingface_hub", "manifest-ml", "nlpcloud", "openai", "openlm", "torch", "transformers"]
 openai = ["openai", "tiktoken"]
 qdrant = ["qdrant-client"]
@@ -11487,4 +11560,4 @@ text-helpers = ["chardet"]
 [metadata]
 lock-version = "2.0"
 python-versions = ">=3.8.1,<4.0"
-content-hash = "91e9b02d57bf4fc7e7ad61d0c2980c0726c8f6a7379876770f644f3e049aba3a"
+content-hash = "d2cc8a1eff7ef67405889cc4d9e81b6c94c9186eba22c27abd5e7da8dcb26949"
diff --git a/pyproject.toml b/pyproject.toml
index 5c784a08c6c2c..921de7c3ba27a 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -110,6 +110,7 @@ awadb = {version = "^0.3.3", optional = true}
 azure-search-documents = {version = "11.4.0a20230509004", source = "azure-sdk-dev", optional = true}
 # now streamlit requires Python >=3.7, !=3.9.7 So, it is commented out.
 #streamlit = {version = "^1.18.0", optional = true}
+sqlfluff = {version = "^2.1.1", optional = true}
 
 
 [tool.poetry.group.docs.dependencies]
@@ -331,7 +332,8 @@ extended_testing = [
 # now streamlit requires Python >=3.7, !=3.9.7 So, it is commented out.
 # "streamlit",
  "pyspark",
- "openai"
+ "openai",
+ "sqlfluff"
 ]
 
 [[tool.poetry.source]]
diff --git a/tests/integration_tests/chains/test_sql_database.py b/tests/integration_tests/chains/test_sql_database.py
index f19ec02594e71..904a7b6c584a3 100644
--- a/tests/integration_tests/chains/test_sql_database.py
+++ b/tests/integration_tests/chains/test_sql_database.py
@@ -1,9 +1,11 @@
 """Test SQL Database Chain."""
-from sqlalchemy import Column, Integer, MetaData, String, Table, create_engine, insert
+import pytest
+from sqlalchemy import Column, Integer, MetaData, String, Table, create_engine
 
 from langchain.chains.sql_database.base import (
     SQLDatabaseChain,
     SQLDatabaseSequentialChain,
+    SQLValidation,
 )
 from langchain.llms.openai import OpenAI
 from langchain.sql_database import SQLDatabase
@@ -19,74 +21,72 @@
 )
 
 
+@pytest.mark.requires("sqlfluff")
 def test_sql_database_run() -> None:
     """Test that commands can be run successfully and returned in correct format."""
     engine = create_engine("sqlite:///:memory:")
     metadata_obj.create_all(engine)
-    stmt = insert(user).values(user_id=13, user_name="Harrison", user_company="Foo")
-    with engine.connect() as conn:
-        conn.execute(stmt)
     db = SQLDatabase(engine)
+    db.run("INSERT INTO user VALUES (13, 'Harrison', 'Foo')")
     db_chain = SQLDatabaseChain.from_llm(OpenAI(temperature=0), db)
     output = db_chain.run("What company does Harrison work at?")
-    expected_output = " Harrison works at Foo."
+    expected_output = "Harrison works at Foo."
     assert output == expected_output
 
 
+@pytest.mark.requires("sqlfluff")
 def test_sql_database_run_update() -> None:
     """Test that update commands run successfully and returned in correct format."""
     engine = create_engine("sqlite:///:memory:")
     metadata_obj.create_all(engine)
-    stmt = insert(user).values(user_id=13, user_name="Harrison", user_company="Foo")
-    with engine.connect() as conn:
-        conn.execute(stmt)
     db = SQLDatabase(engine)
-    db_chain = SQLDatabaseChain.from_llm(OpenAI(temperature=0), db)
+    db.run("INSERT INTO user VALUES (13, 'Harrison', 'Foo')")
+    db_chain = SQLDatabaseChain.from_llm(
+        OpenAI(temperature=0),
+        db,
+        sql_validation=SQLValidation(allow_non_select_statements=True),
+    )
     output = db_chain.run("Update Harrison's workplace to Bar")
-    expected_output = " Harrison's workplace has been updated to Bar."
+    expected_output = "Harrison's workplace has been updated to Bar."
     assert output == expected_output
     output = db_chain.run("What company does Harrison work at?")
-    expected_output = " Harrison works at Bar."
+    expected_output = "Harrison works at Bar."
     assert output == expected_output
 
 
+@pytest.mark.requires("sqlfluff")
 def test_sql_database_sequential_chain_run() -> None:
     """Test that commands can be run successfully SEQUENTIALLY
     and returned in correct format."""
     engine = create_engine("sqlite:///:memory:")
     metadata_obj.create_all(engine)
-    stmt = insert(user).values(user_id=13, user_name="Harrison", user_company="Foo")
-    with engine.connect() as conn:
-        conn.execute(stmt)
     db = SQLDatabase(engine)
+    db.run("INSERT INTO user VALUES (13, 'Harrison', 'Foo')")
     db_chain = SQLDatabaseSequentialChain.from_llm(OpenAI(temperature=0), db)
     output = db_chain.run("What company does Harrison work at?")
-    expected_output = " Harrison works at Foo."
+    expected_output = "Harrison works at Foo."
     assert output == expected_output
 
 
+@pytest.mark.requires("sqlfluff")
 def test_sql_database_sequential_chain_intermediate_steps() -> None:
     """Test that commands can be run successfully SEQUENTIALLY and returned
     in correct format. sWith Intermediate steps"""
     engine = create_engine("sqlite:///:memory:")
     metadata_obj.create_all(engine)
-    stmt = insert(user).values(user_id=13, user_name="Harrison", user_company="Foo")
-    with engine.connect() as conn:
-        conn.execute(stmt)
     db = SQLDatabase(engine)
+    db.run("INSERT INTO user VALUES (13, 'Harrison', 'Foo')")
     db_chain = SQLDatabaseSequentialChain.from_llm(
         OpenAI(temperature=0), db, return_intermediate_steps=True
     )
     output = db_chain("What company does Harrison work at?")
-    expected_output = " Harrison works at Foo."
+    expected_output = "Harrison works at Foo."
     assert output["result"] == expected_output
 
-    query = output["intermediate_steps"][0]
-    expected_query = (
-        " SELECT user_company FROM user WHERE user_name = 'Harrison' LIMIT 1;"
-    )
+    query = output["intermediate_steps"][1]
+    expected_query = "SELECT user_company FROM user WHERE user_name = 'Harrison';"
     assert query == expected_query
 
-    query_results = output["intermediate_steps"][1]
+    query_results = output["intermediate_steps"][3]
     expected_query_results = "[('Foo',)]"
     assert query_results == expected_query_results
diff --git a/tests/unit_tests/chains/test_sql.py b/tests/unit_tests/chains/test_sql.py
new file mode 100644
index 0000000000000..bcde12a25f70a
--- /dev/null
+++ b/tests/unit_tests/chains/test_sql.py
@@ -0,0 +1,179 @@
+"""Test SQL functionality."""
+import pytest
+
+from langchain import SQLDatabase
+from langchain.chains.sql_database.base import (
+    SQLDatabaseChain,
+    SQLValidation,
+    validate_sql,
+)
+from langchain.chains.sql_database.prompt import SQL_PROMPTS, SQLITE_PROMPT
+from tests.unit_tests.llms.fake_llm import FakeLLM
+
+_SQLITE_IN_MEMORY_DB_URI = "sqlite://"
+
+
+# Arrange
+@pytest.fixture
+def in_memory_db() -> SQLDatabase:
+    database = SQLDatabase.from_uri(_SQLITE_IN_MEMORY_DB_URI)
+
+    # Create table 'users'
+    database.run(
+        """
+        CREATE TABLE users (
+            id INTEGER PRIMARY KEY,
+            name TEXT,
+            email TEXT UNIQUE
+        )
+    """
+    )
+
+    # Create table 'orders'
+    database.run(
+        """
+        CREATE TABLE orders (
+            id INTEGER PRIMARY KEY,
+            user_id INTEGER,
+            product_name TEXT,
+            FOREIGN KEY (user_id) REFERENCES users(id)
+        )
+    """
+    )
+
+    # Create table 'products'
+    database.run(
+        """
+        CREATE TABLE products (
+            id INTEGER PRIMARY KEY,
+            name TEXT,
+            price REAL
+        )
+    """
+    )
+
+    # Insert some stub data
+    [
+        database.run(
+            f"INSERT INTO users VALUES ({value[0]}, '{value[1]}', '{value[2]}')"
+        )
+        for value in [
+            (1, "John Doe", "john@example.com"),
+            (2, "Jane Doe", "jane@example.com"),
+        ]
+    ]
+    [
+        database.run(
+            f"INSERT INTO orders VALUES ({value[0]}, {value[1]}, '{value[2]}')"
+        )
+        for value in [(1, 1, "iPhone 13"), (2, 2, "Samsung Galaxy S21")]
+    ]
+    [
+        database.run(
+            f"INSERT INTO products VALUES ({value[0]}, '{value[1]}', {value[2]})"
+        )
+        for value in [(1, "iPhone 13", 999.99), (2, "Samsung Galaxy S21", 799.99)]
+    ]
+
+    return database
+
+
+@pytest.mark.requires("sqlfluff")
+def test_simple_question(in_memory_db: SQLDatabase) -> None:
+    question = "What is the price of an iphone?"
+    prompt = SQLITE_PROMPT.format(top_k=5, input=question, table_info="")
+    queries = {
+        f"{prompt}\nSQLQuery:": "SELECT price FROM products WHERE name like '%iphone%'",
+        f"{prompt}\nSQLQuery:SELECT price FROM products WHERE name like '%iphone%'\n"
+        "SQLResult: [(999.99,)]\nAnswer:": "999.99\n",
+    }
+    fake_llm = FakeLLM(queries=queries)
+    fake_llm_db_chain = SQLDatabaseChain.from_llm(fake_llm, in_memory_db)
+    output = fake_llm_db_chain.run(question)
+    assert output == "999.99"
+
+
+@pytest.mark.requires("sqlfluff")
+def test_malformed_sql_query() -> None:
+    with pytest.raises(ValueError):
+        validate_sql(
+            "SELECT price FROM products WHERE name like %iphone%",
+            "sqlite",
+            SQLValidation(),
+        )
+
+
+@pytest.mark.requires("sqlfluff")
+def test_invalid_sql_dialect() -> None:
+    with pytest.warns(
+        match="Dialect xxxxxxxxxxxxxx unsupported for SQL validation. "
+        "No validation will be done. Go to "
+        "https://docs.sqlfluff.com/en/stable/dialects.html to see supported dialects"
+    ):
+        validate_sql(
+            "SELECT price FROM products WHERE name like %iphone%",
+            "xxxxxxxxxxxxxx",
+            SQLValidation(),
+        )
+    with pytest.raises(ValueError):
+        validate_sql(
+            "SELECT price FROM products WHERE name like %iphone%",
+            "xxxxxxxxxxxxxx",
+            SQLValidation(allow_unsupported_dialect=False),
+        )
+
+
+@pytest.mark.requires("sqlfluff")
+def test_non_select_sql_query() -> None:
+    validate_sql(
+        "DROP TABLE products", "sqlite", SQLValidation(allow_non_select_statements=True)
+    )
+
+    with pytest.raises(ValueError):
+        validate_sql(
+            "DROP TABLE products",
+            "sqlite",
+            SQLValidation(allow_non_select_statements=False),
+        )
+    with pytest.raises(ValueError):
+        validate_sql(
+            "INSERT INTO users (user_id, user_name) VALUES (2, 'Sam');",
+            "sqlite",
+            SQLValidation(allow_non_select_statements=False),
+        )
+
+
+@pytest.mark.requires("sqlfluff")
+def test_select_all_sql_query() -> None:
+    validate_sql(
+        "SELECT * FROM users;",
+        "sqlite",
+        SQLValidation(allow_select_all_statements=True),
+    )
+    with pytest.raises(ValueError):
+        validate_sql(
+            "SELECT * FROM users;",
+            "sqlite",
+            SQLValidation(allow_select_all_statements=False),
+        )
+
+
+@pytest.mark.requires("sqlfluff")
+@pytest.mark.filterwarnings("ignore::UserWarning")
+def test_parsing_all_supported_dialects() -> None:
+    sql_statements = [
+        "SELECT * FROM users;",
+        "SELECT user_id, user_name FROM users;",
+        "SELECT * FROM users WHERE user_id = 1;",
+        "SELECT COUNT(*) FROM users GROUP BY user_id;",
+        "INSERT INTO users (user_id, user_name) VALUES (2, 'Sam');",
+    ]
+    for sql_stamenet in sql_statements:
+        for dialect in SQL_PROMPTS.keys():
+            validate_sql(
+                sql_stamenet,
+                dialect,
+                SQLValidation(
+                    allow_unsupported_dialect=True, allow_non_select_statements=True
+                ),
+            )