88
99import sqlbuild ._native as _native
1010from sqlbuild .compiler .sql_analysis .exceptions import SqlAnalysisBoundaryError
11+ from sqlbuild .compiler .sql_analysis .main .import_polyglot_sql import import_polyglot_sql
1112from sqlbuild .compiler .sql_analysis .models import (
1213 SqlBindingDiagnostic ,
1314 SqlBindingResult ,
2526_ERROR_SEVERITY : str = "error"
2627_ORDER_BY_CONTEXT : str = "ORDER BY"
2728_WINDOW_ORDER_BY_CONTEXT : str = "WINDOW ORDER BY"
29+ _SNOWFLAKE_DIALECT : str = "snowflake"
30+ _SELECT_KIND : str = "select"
31+ _IMPLICIT_VALUES_COLUMN_PATTERN : re .Pattern [str ] = re .compile (
32+ r"^Unknown column '(column([1-9][0-9]*))'$" ,
33+ flags = re .IGNORECASE ,
34+ )
2835
2936
3037def get_schema_validations (
@@ -43,7 +50,7 @@ def get_schema_validations(
4350 "native SQL schema validation returned an invalid batch response"
4451 )
4552 return tuple (
46- _binding_result (sql = request .sql , response = response )
53+ _binding_result (sql = request .sql , dialect = request . dialect , response = response )
4754 for request , response in zip (requests , responses , strict = True )
4855 )
4956
@@ -73,7 +80,7 @@ def _request_payload(*, request: SqlSchemaValidationRequest) -> dict[str, object
7380 }
7481
7582
76- def _binding_result (* , sql : str , response : object ) -> SqlBindingResult :
83+ def _binding_result (* , sql : str , dialect : str | None , response : object ) -> SqlBindingResult :
7784 response_dict : dict [str , object ] | None = (
7885 cast (dict [str , object ], response ) if isinstance (response , dict ) else None
7986 )
@@ -99,6 +106,14 @@ def _binding_result(*, sql: str, response: object) -> SqlBindingResult:
99106 line = _optional_int (value_dict .get ("line" )),
100107 column = _optional_int (value_dict .get ("column" )),
101108 )
109+ if _is_proven_snowflake_values_column (
110+ sql = sql ,
111+ dialect = dialect ,
112+ message = raw_message ,
113+ line = line ,
114+ column = column ,
115+ ):
116+ continue
102117 diagnostics .append (
103118 SqlBindingDiagnostic (
104119 code = _SQLBUILD_CODE_BY_NATIVE_CODE [native_code ],
@@ -112,6 +127,138 @@ def _binding_result(*, sql: str, response: object) -> SqlBindingResult:
112127 return SqlBindingResult (diagnostics = tuple (diagnostics ))
113128
114129
130+ def _is_proven_snowflake_values_column (
131+ * ,
132+ sql : str ,
133+ dialect : str | None ,
134+ message : str ,
135+ line : int | None ,
136+ column : int | None ,
137+ ) -> bool :
138+ if (dialect or "" ).lower () != _SNOWFLAKE_DIALECT or line is None or column is None :
139+ return False
140+ match : re .Match [str ] | None = _IMPLICIT_VALUES_COLUMN_PATTERN .fullmatch (message )
141+ if match is None :
142+ return False
143+ column_name : str = match .group (1 )
144+ ordinal : int = int (match .group (2 ))
145+ polyglot_module : Any = import_polyglot_sql ()
146+ try :
147+ parsed : Any = polyglot_module .parse_one (sql , dialect = _SNOWFLAKE_DIALECT )
148+ except polyglot_module .PolyglotError :
149+ return False
150+ selects : list [Any ] = []
151+ if str (getattr (parsed , "kind" , "" )) == _SELECT_KIND :
152+ selects .append (parsed )
153+ selects .extend (parsed .find_all (_SELECT_KIND ))
154+ return any (
155+ _select_proves_implicit_values_column (
156+ select = select ,
157+ sql = sql ,
158+ column_name = column_name ,
159+ ordinal = ordinal ,
160+ line = line ,
161+ column = column ,
162+ )
163+ for select in selects
164+ )
165+
166+
167+ def _select_proves_implicit_values_column (
168+ * ,
169+ select : Any ,
170+ sql : str ,
171+ column_name : str ,
172+ ordinal : int ,
173+ line : int ,
174+ column : int ,
175+ ) -> bool :
176+ payload : object = select .to_dict ().get (_SELECT_KIND )
177+ if not isinstance (payload , dict ):
178+ return False
179+ select_payload : dict [str , object ] = cast (dict [str , object ], payload )
180+ if select_payload .get ("joins" ):
181+ return False
182+ from_payload : object = select_payload .get ("from" )
183+ if not isinstance (from_payload , dict ):
184+ return False
185+ relations : object = cast (dict [str , object ], from_payload ).get ("expressions" )
186+ if not isinstance (relations , list ) or len (relations ) != 1 :
187+ return False
188+ relation : object = relations [0 ]
189+ if not isinstance (relation , dict ):
190+ return False
191+ values : object = cast (dict [str , object ], relation ).get ("values" )
192+ if not isinstance (values , dict ):
193+ return False
194+ values_payload : dict [str , object ] = cast (dict [str , object ], values )
195+ if values_payload .get ("alias" ) is not None or values_payload .get ("column_aliases" ):
196+ return False
197+ rows : object = values_payload .get ("expressions" )
198+ if not isinstance (rows , list ) or not rows :
199+ return False
200+ row_widths : list [int ] = []
201+ for row in rows :
202+ if not isinstance (row , dict ):
203+ return False
204+ expressions : object = cast (dict [str , object ], row ).get ("expressions" )
205+ if not isinstance (expressions , list ):
206+ return False
207+ row_widths .append (len (expressions ))
208+ if any (width < ordinal for width in row_widths ):
209+ return False
210+ projections : object = select_payload .get ("expressions" )
211+ if not isinstance (projections , list ):
212+ return False
213+ return any (
214+ _projection_is_implicit_values_column (
215+ projection = projection ,
216+ sql = sql ,
217+ column_name = column_name ,
218+ line = line ,
219+ column = column ,
220+ )
221+ for projection in projections
222+ )
223+
224+
225+ def _projection_is_implicit_values_column (
226+ * , projection : object , sql : str , column_name : str , line : int , column : int
227+ ) -> bool :
228+ if not isinstance (projection , dict ):
229+ return False
230+ expression : object = projection
231+ alias_payload : object = cast (dict [str , object ], projection ).get ("alias" )
232+ if isinstance (alias_payload , dict ):
233+ expression = cast (dict [str , object ], alias_payload ).get ("this" )
234+ if not isinstance (expression , dict ):
235+ return False
236+ column_payload : object = cast (dict [str , object ], expression ).get ("column" )
237+ if not isinstance (column_payload , dict ):
238+ return False
239+ column_dict : dict [str , object ] = cast (dict [str , object ], column_payload )
240+ if column_dict .get ("table" ) is not None :
241+ return False
242+ name_payload : object = column_dict .get ("name" )
243+ if not isinstance (name_payload , dict ):
244+ return False
245+ name_dict : dict [str , object ] = cast (dict [str , object ], name_payload )
246+ if (
247+ name_dict .get ("quoted" ) is True
248+ or str (name_dict .get ("name" ) or "" ).lower () != column_name .lower ()
249+ ):
250+ return False
251+ span : object = column_dict .get ("span" )
252+ if not isinstance (span , dict ):
253+ return False
254+ start : object = cast (dict [str , object ], span ).get ("start" )
255+ if not isinstance (start , int ):
256+ return False
257+ candidate_line : int = sql .count ("\n " , 0 , start ) + 1
258+ candidate_column : int = start - sql .rfind ("\n " , 0 , start )
259+ return (candidate_line , candidate_column ) == (line , column )
260+
261+
115262def _optional_int (value : object ) -> int | None :
116263 return value if isinstance (value , int ) else None
117264
0 commit comments