|
73 | 73 | _KERNEL_MANAGED_HEADERS = frozenset({"authorization", "x-databricks-org-id"}) |
74 | 74 |
|
75 | 75 |
|
| 76 | +# Leading verbs of SQL volume/staging statements. Detected by the |
| 77 | +# leading token (case-insensitive) so the kernel backend can fail loud |
| 78 | +# on staging ops it can't service — see ``execute_command``. |
| 79 | +_STAGING_VERBS = ("PUT", "GET", "REMOVE") |
| 80 | + |
| 81 | + |
76 | 82 | def _strip_leading_sql_comments(sql: str) -> str: |
77 | | - """Skip SQL comments so unsupported transfers cannot appear successful.""" |
| 83 | + """Strip leading whitespace and SQL comments (``-- …`` line and |
| 84 | + ``/* … */`` block, possibly several) from ``sql``, returning the |
| 85 | + remainder. |
| 86 | +
|
| 87 | + Needed so staging detection sees the real leading verb: a |
| 88 | + comment-prefixed staging op (``-- upload\\nPUT …`` or |
| 89 | + ``/* c */ PUT …``, common in ETL scripts) must still be classified |
| 90 | + as staging, or it would slip past the guard into the silent-no-op |
| 91 | + bug. Block comments do not nest in Databricks SQL, so a simple |
| 92 | + scan-to-``*/`` is correct. |
| 93 | + """ |
78 | 94 | i = 0 |
79 | 95 | n = len(sql) |
80 | 96 | while i < n: |
81 | 97 | if sql[i].isspace(): |
82 | 98 | i += 1 |
83 | 99 | elif sql.startswith("--", i): |
| 100 | + # Line comment: skip to end of line (or string). |
84 | 101 | nl = sql.find("\n", i) |
85 | 102 | i = n if nl == -1 else nl + 1 |
86 | 103 | elif sql.startswith("/*", i): |
| 104 | + # Block comment: skip to closing */ (or end if unterminated). |
87 | 105 | close = sql.find("*/", i + 2) |
88 | 106 | i = n if close == -1 else close + 2 |
89 | 107 | else: |
90 | 108 | break |
91 | 109 | return sql[i:] |
92 | 110 |
|
93 | 111 |
|
94 | | -def _is_staging_statement(operation: str) -> bool: |
95 | | - stripped = _strip_leading_sql_comments(operation) |
96 | | - verb = stripped.split(None, 1)[0].upper() if stripped.strip() else "" |
97 | | - return verb in ("PUT", "GET", "REMOVE") |
98 | | - |
99 | | - |
100 | 112 | def _is_not_found(exc: BaseException) -> bool: |
101 | 113 | """True iff ``exc`` is a kernel ``NotFound`` error (HTTP 404 / |
102 | 114 | ``STATEMENT_NOT_FOUND``). |
@@ -132,6 +144,21 @@ def _catalog_or_none(value: Optional[str]) -> Optional[str]: |
132 | 144 | return value |
133 | 145 |
|
134 | 146 |
|
| 147 | +def _is_staging_statement(operation: str) -> bool: |
| 148 | + """True iff ``operation`` is a volume/staging statement (PUT / GET / |
| 149 | + REMOVE). |
| 150 | +
|
| 151 | + Strips leading whitespace + SQL comments first (so a comment- |
| 152 | + prefixed staging op is still caught), then matches the leading token |
| 153 | + only — so a normal query that merely *contains* the word (e.g. |
| 154 | + ``SELECT 'GET' AS x``) isn't misflagged. |
| 155 | + """ |
| 156 | + stripped = _strip_leading_sql_comments(operation) |
| 157 | + # First whitespace-delimited token, uppercased. |
| 158 | + verb = stripped.split(None, 1)[0].upper() if stripped.strip() else "" |
| 159 | + return verb in _STAGING_VERBS |
| 160 | + |
| 161 | + |
135 | 162 | def _kernel_session_accepts_kwarg(name: str) -> bool: |
136 | 163 | """True iff the installed ``databricks_sql_kernel.Session`` constructor |
137 | 164 | declares keyword ``name``. |
|
0 commit comments