googleapis/mcp-toolbox · error

unanalyzable statements like '%s %s' are not allowed

Error message

unanalyzable statements like '%s %s' are not allowed

What it means

parseSQL blocks statements it cannot safely analyze under dataset restrictions. CREATE/CREATE OR REPLACE of a PROCEDURE or FUNCTION embeds arbitrary SQL bodies that the parser cannot inspect, so it rejects the statement to ensure every table reference remains verifiable.

Source

Thrown at internal/tools/bigquery/bigquerycommon/table_name_parser.go:271

					}
					if infoSchemaIdx > 2 {
						return 0, fmt.Errorf("invalid INFORMATION_SCHEMA query path %q", strings.Join(parts, "."))
					}
					parts = parts[:infoSchemaIdx+1]
				}

				if len(parts) == 1 {
					keyword := strings.ToLower(parts[0])
					switch keyword {
					case "call":
						return 0, fmt.Errorf("CALL is not allowed when dataset restrictions are in place, as the called procedure's contents cannot be safely analyzed")
					case "immediate":
						if lastToken == "execute" {
							return 0, fmt.Errorf("EXECUTE IMMEDIATE is not allowed when dataset restrictions are in place, as its contents cannot be safely analyzed")
						}
					case "procedure", "function":
						if lastToken == "create" || lastToken == "create or replace" {
							return 0, fmt.Errorf("unanalyzable statements like '%s %s' are not allowed", strings.ToUpper(lastToken), strings.ToUpper(keyword))
						}
					case verbCreate, verbAlter, verbDrop, verbSelect, verbInsert, verbUpdate, verbDelete, verbMerge:
						if statementVerb == "" {
							statementVerb = keyword
						}
					}

					if statementVerb == verbCreate || statementVerb == verbAlter || statementVerb == verbDrop {
						if keyword == "schema" || keyword == "dataset" {
							return 0, fmt.Errorf("dataset-level operations like '%s %s' are not allowed when dataset restrictions are in place", strings.ToUpper(statementVerb), strings.ToUpper(keyword))
						}
					}

					if _, ok := tableFollowsKeywords[keyword]; ok {
						expectingTable = true
						lastTableKeyword = keyword
					} else if _, ok := tableContextExitKeywords[keyword]; ok {
						expectingTable = false

View on GitHub (pinned to 8cc6e09de2)

Solutions

  1. Split routine-creation DDL out of the restricted path and run it separately without dataset restrictions
  2. Remove the CREATE PROCEDURE/FUNCTION statement from the validated query
  3. If the routine is already deployed, just call CALL proc() only if the procedure itself is allowed, or pre-create it via admin tooling

Example fix

// before
CREATE FUNCTION myfunc(x INT64) AS (x * 2);
SELECT myfunc(id) FROM proj.ds.t;
// after
-- create routine via unrestricted admin path first, then:
SELECT myfunc(id) FROM proj.ds.t;
Defensive patterns

Strategy: validation

Validate before calling

u := strings.ToUpper(sql)
for _, bad := range []string{"CREATE PROCEDURE", "CREATE FUNCTION", "CREATE OR REPLACE PROCEDURE", "CREATE OR REPLACE FUNCTION"} {
    if strings.Contains(u, bad) {
        return fmt.Errorf("query rejected: %s is not permitted under dataset restrictions", bad)
    }
}

Try / catch

_, err := parser.Parse(sql)
if err != nil && strings.Contains(err.Error(), "unanalyzable statements") {
    // route routine DDL to an unrestricted admin path
    return handleRoutineDDL(sql)
}

Prevention

When it happens

Trigger: Running a CREATE PROCEDURE, CREATE FUNCTION, CREATE OR REPLACE PROCEDURE, or CREATE OR REPLACE FUNCTION statement through TableParser/parseSQL while dataset restrictions are active.

Common situations: Setup/migration scripts that define routines before running queries; users trying to deploy UDFs or procedures through a restricted query tool; CI jobs that run DDL scripts through the same connection.

Related errors


AI-assisted analysis of googleapis/mcp-toolbox@8cc6e09de2 (2026-09-05). Data as JSON: /api/errors/ebef5d31a73f9852. Report an issue: GitHub.