@@ -95,6 +95,41 @@ func (p *Parser) parseFromTableReference() (ast.TableReference, error) {
9595 tableRef .TableFunc = funcCall
9696 }
9797
98+ // Snowflake / ANSI SAMPLE or TABLESAMPLE clause on a table reference:
99+ // SAMPLE [BERNOULLI | SYSTEM | BLOCK | ROW] (N [ROWS])
100+ // TABLESAMPLE [method] (N [ROWS])
101+ // Consume permissively — the method and paren block are consumed
102+ // but not yet modeled on the AST.
103+ if strings .EqualFold (p .currentToken .Token .Value , "SAMPLE" ) ||
104+ strings .EqualFold (p .currentToken .Token .Value , "TABLESAMPLE" ) {
105+ p .advance () // SAMPLE / TABLESAMPLE
106+ // Optional method name
107+ upper := strings .ToUpper (p .currentToken .Token .Value )
108+ if upper == "BERNOULLI" || upper == "SYSTEM" || upper == "BLOCK" || upper == "ROW" {
109+ p .advance ()
110+ }
111+ // (N [ROWS]) block
112+ if p .isType (models .TokenTypeLParen ) {
113+ depth := 0
114+ for {
115+ t := p .currentToken .Token .Type
116+ if t == models .TokenTypeEOF {
117+ break
118+ }
119+ if t == models .TokenTypeLParen {
120+ depth ++
121+ } else if t == models .TokenTypeRParen {
122+ depth --
123+ if depth == 0 {
124+ p .advance ()
125+ break
126+ }
127+ }
128+ p .advance ()
129+ }
130+ }
131+ }
132+
98133 // Snowflake time-travel / change-tracking clauses:
99134 // AT (TIMESTAMP => ...)
100135 // BEFORE (STATEMENT => ...)
@@ -113,7 +148,7 @@ func (p *Parser) parseFromTableReference() (ast.TableReference, error) {
113148 // Similarly, START followed by WITH is a hierarchical query seed, not an alias.
114149 // Don't consume PIVOT/UNPIVOT as a table alias — they are contextual
115150 // keywords in SQL Server/Oracle and must reach the pivot-clause parser below.
116- if (p .isIdentifier () || p .isType (models .TokenTypeAs )) && ! p .isMariaDBClauseStart () && ! p .isPivotKeyword () && ! p .isUnpivotKeyword () && ! p .isQualifyKeyword () && ! p .isMinusSetOp () && ! p .isSnowflakeTimeTravelStart () {
151+ if (p .isIdentifier () || p .isType (models .TokenTypeAs )) && ! p .isMariaDBClauseStart () && ! p .isPivotKeyword () && ! p .isUnpivotKeyword () && ! p .isQualifyKeyword () && ! p .isMinusSetOp () && ! p .isSnowflakeTimeTravelStart () && ! p . isSampleKeyword () {
117152 if p .isType (models .TokenTypeAs ) {
118153 p .advance () // Consume AS
119154 if ! p .isIdentifier () {
@@ -227,7 +262,7 @@ func (p *Parser) parseJoinedTableRef(joinType string) (ast.TableReference, error
227262 // Similarly, START followed by WITH is a hierarchical query seed, not an alias.
228263 // Don't consume PIVOT/UNPIVOT as a table alias — they are contextual
229264 // keywords in SQL Server/Oracle and must reach the pivot-clause parser below.
230- if (p .isIdentifier () || p .isType (models .TokenTypeAs )) && ! p .isMariaDBClauseStart () && ! p .isPivotKeyword () && ! p .isUnpivotKeyword () && ! p .isQualifyKeyword () && ! p .isMinusSetOp () && ! p .isSnowflakeTimeTravelStart () {
265+ if (p .isIdentifier () || p .isType (models .TokenTypeAs )) && ! p .isMariaDBClauseStart () && ! p .isPivotKeyword () && ! p .isUnpivotKeyword () && ! p .isQualifyKeyword () && ! p .isMinusSetOp () && ! p .isSnowflakeTimeTravelStart () && ! p . isSampleKeyword () {
231266 if p .isType (models .TokenTypeAs ) {
232267 p .advance ()
233268 if ! p .isIdentifier () {
0 commit comments