001package gudusoft.gsqlparser.nodes; 002 003import gudusoft.gsqlparser.*; 004 005import java.util.ArrayList; 006 007public class TTableFunction extends TFunctionCall implements IRelation { 008 ArrayList<TAttributeNode> relationAttributes = new ArrayList<>(); 009 010 @Override 011 public int size(){ 012 return relationAttributes.size(); 013 } 014 015 @Override 016 public ArrayList<TAttributeNode> getAttributes(){ 017 return relationAttributes; 018 }; 019 020 @Override 021 public String getRelationName(){ 022 return this.getFunctionName().toString(); 023 }; 024 025 public void initAttributes(){ 026 if (fieldDefs == null) return; 027 int c = 0; 028 TResultColumn rc = null; 029 for(TColumnDefinition cd:fieldDefs){ 030 if (fieldValues != null){ 031 rc = fieldValues.getResultColumn(c); 032 c++; 033 } 034 TAttributeNode node = new TAttributeNode(cd.getColumnName().toString(),null,cd,rc); 035 //relationAttributes.add(node); 036 TAttributeNode.addNodeToList(node,relationAttributes); 037 } 038 } 039 040 041 042 043 public void init(Object arg1,Object arg2,Object arg3){ 044 init(arg1,arg2); 045 switch (functionType){ 046 case struct_t: 047 this.fieldValues = (TResultColumnList)arg3; 048 break; 049 } 050 } 051 052 public void init(Object arg1,Object arg2,Object arg3,Object arg4){ 053 init(arg1,arg2,arg3); 054 switch (functionType){ 055 case struct_t: 056 this.fieldDefs = (TColumnDefinitionList) arg4; 057 break; 058 } 059 } 060 061 public void doParse(TCustomSqlStatement psql, ESqlClause plocation){ 062 this.dbvendor = psql.dbvendor; 063 switch(this.getFunctionType()){ 064 case unknown_t: 065 case chr_t: 066 case udf_t: 067 case builtin_t: 068 case listagg_t: 069 case year_t: 070 if (Args != null){ 071 boolean check_first_arg = true; 072 if ((psql.dbvendor == EDbVendor.dbvsybase)||(psql.dbvendor == EDbVendor.dbvmssql)){ 073 check_first_arg = !((functionName.toString().equalsIgnoreCase("dateadd")) 074 ||(functionName.toString().equalsIgnoreCase("datediff")) 075 ||(functionName.toString().equalsIgnoreCase("datename")) 076 ||(functionName.toString().equalsIgnoreCase("datepart")) 077 ); 078 } 079 for(int i=0;i<Args.size();i++){ 080 if ((!check_first_arg)&&(i==0)) { 081 // mark the first argument in datediff to 082 setFirstArgAsDateTimePart(i); 083 continue; 084 } 085 Args.getExpression(i).doParse(psql,plocation); 086 } 087 } 088 089 if (psql.dbvendor == EDbVendor.dbvmssql){ 090 // check OGC function of sql server 091// System.out.println("func:"+functionName.toString()); 092 if (isSQLServerOGCMethod(functionName.getObjectToken())){ 093 if (functionName.getSchemaToken() != null){ 094 functionName.getSchemaToken().setDbObjType(TObjectName.ttobjColumn); 095 if (functionName.getDatabaseToken() != null){ 096 functionName.getDatabaseToken().setDbObjType(TObjectName.ttobjTable); 097 } 098 099 TObjectName objectName = new TObjectName(); 100 objectName.init(functionName.getDatabaseToken(),functionName.getSchemaToken()); 101 objectName.setGsqlparser(psql.getGsqlparser()); // this will make toString work correctly 102 psql.linkColumnReferenceToTable(objectName,plocation); 103 psql.linkColumnToTable(objectName,plocation); 104 } 105 }else if (isSQLServerFunctionsONXMLColumn()) { 106 107 108 109 } 110 } else if (psql.dbvendor == EDbVendor.dbvoracle){ 111 if (plocation == ESqlClause.spAssignValue){ 112 if (functionName.getNumberOfPart() == 3){ 113 // schema1.pkg1.GETCUSTOMERNAME(2) 114 functionName.setPackageToken(functionName.getSchemaToken()); 115 functionName.setSchemaToken(functionName.getDatabaseToken()); 116 functionName.setServerToken(null); 117 } 118 } 119 } else if (psql.dbvendor == EDbVendor.dbvteradata){ 120 // Handle NPath function - extract ON clause tables for data lineage 121 if (functionName != null && functionName.toString().equalsIgnoreCase("NPath")){ 122 analyzeTeradataNPathOnClause(psql, plocation); 123 } 124 } 125 break; 126 127 case xmlquery_t: 128 if (xmlPassingClause != null){ 129 xmlPassingClause.doParse(psql,plocation); 130 } 131 break; 132 133 case struct_t: 134 if (fieldValues != null){ 135 fieldValues.doParse(psql,plocation); 136 } 137 if (fieldDefs != null){ 138 fieldDefs.doParse(psql,plocation); 139 } 140 break; 141 case if_t: 142 expr1.doParse(psql,plocation); 143 expr2.doParse(psql,plocation); 144 expr3.doParse(psql,plocation); 145 break; 146 case array_t: // bigquery array (subquery) 147 this.getArgs().getExpression(0).doParse(psql,plocation); 148 break; 149 case array_agg_t: 150 this.getArgs().getExpression(0).doParse(psql,plocation); 151 break; 152 default:; 153 } 154 155 if (withinGroup != null){ 156 withinGroup.doParse(psql,plocation); 157 } 158 if (analyticFunction != null){ 159 analyticFunction.doParse(psql,plocation); 160 } 161 162 if (filterClause != null){ 163 filterClause.doParse(psql,plocation); 164 } 165 166 if (windowDef != null){ 167 windowDef.doParse(psql,plocation); 168 } 169 } 170 171 public void accept(TParseTreeVisitor v){ 172 v.preVisit(this); 173 v.postVisit(this); 174 } 175 176 public void acceptChildren(TParseTreeVisitor v){ 177 v.preVisit(this); 178 this.getFunctionName().acceptChildren(v); 179 switch(this.getFunctionType()){ 180 case unknown_t: 181 if (this.getArgs() != null){ 182 this.getArgs().acceptChildren(v); 183 } 184 break; 185 case udf_t: 186 if (this.getArgs() != null){ 187 this.getArgs().acceptChildren(v); 188 } 189 break; 190 case case_n_t: 191 this.getArgs().acceptChildren(v); 192 break; 193 194 case xmlquery_t: 195 break; 196 case xmlcast_t: 197 break; 198 case match_against_t: 199 //for(int i=0;i<this.getMatchColumns().size();i++){ 200 // psql.linkColumnReferenceToTable(this.getMatchColumns().getObjectName(i),plocation); 201 //} 202 this.getAgainstExpr().acceptChildren(v); 203 break; 204 case struct_t: 205 if (fieldValues != null){ 206 fieldValues.acceptChildren(v); 207 } 208 if (fieldDefs != null){ 209 fieldDefs.acceptChildren(v); 210 } 211 break; 212 213 default: 214 if (this.getArgs() != null){ 215 this.getArgs().acceptChildren(v); 216 } 217 break; 218 } 219 220 if (this.getAnalyticFunction() != null){ 221 this.getAnalyticFunction().acceptChildren(v); 222 } 223 224 if (this.getFilterClause() != null){ 225 this.getFilterClause().acceptChildren(v); 226 } 227 228 if (this.getWindowDef() != null){ 229 this.getWindowDef().acceptChildren(v); 230 } 231 232 v.postVisit(this); 233 234 } 235 236 /** 237 * Extracts and analyzes tables from Teradata NPath function's ON clause. 238 * The NPath function syntax is: NPath(ON table_or_subquery PARTITION BY ... USING ...) 239 * This method parses the ON clause to extract table references for data lineage. 240 */ 241 private void analyzeTeradataNPathOnClause(TCustomSqlStatement psql, ESqlClause plocation) { 242 String funcText = this.toString(); 243 if (funcText == null || funcText.isEmpty()) { 244 return; 245 } 246 247 // Find the ON clause 248 String upperFuncText = funcText.toUpperCase(); 249 int onIndex = upperFuncText.indexOf(" ON "); 250 if (onIndex < 0) { 251 onIndex = upperFuncText.indexOf("(ON "); 252 if (onIndex >= 0) { 253 onIndex++; // Skip the opening parenthesis 254 } 255 } 256 if (onIndex < 0) { 257 return; 258 } 259 260 // Extract text after ON 261 String afterOn = funcText.substring(onIndex + 4).trim(); 262 if (afterOn.isEmpty()) { 263 return; 264 } 265 266 try { 267 if (afterOn.startsWith("(")) { 268 // It's a subquery - find matching closing parenthesis 269 int depth = 0; 270 int endPos = -1; 271 for (int i = 0; i < afterOn.length(); i++) { 272 char c = afterOn.charAt(i); 273 if (c == '(') depth++; 274 else if (c == ')') { 275 depth--; 276 if (depth == 0) { 277 endPos = i + 1; 278 break; 279 } 280 } 281 } 282 if (endPos > 0) { 283 String subqueryText = afterOn.substring(0, endPos); 284 // Parse the subquery to extract tables 285 TGSqlParser subParser = new TGSqlParser(EDbVendor.dbvteradata); 286 subParser.sqltext = "SELECT * FROM " + subqueryText + " __npath_on"; 287 if (subParser.parse() == 0 && subParser.sqlstatements.size() > 0) { 288 gudusoft.gsqlparser.stmt.TSelectSqlStatement subSelect = 289 (gudusoft.gsqlparser.stmt.TSelectSqlStatement) subParser.sqlstatements.get(0); 290 if (subSelect.tables != null && subSelect.tables.size() > 0) { 291 TTable onTable = subSelect.tables.getTable(0); 292 // If it has a subquery, analyze its tables 293 if (onTable.getSubquery() != null) { 294 for (int i = 0; i < onTable.getSubquery().tables.size(); i++) { 295 TTable sourceTable = onTable.getSubquery().tables.getTable(i); 296 // Add to the NPath function's internal tables list 297 addNPathSourceTable(sourceTable, psql); 298 } 299 } 300 } 301 } 302 } 303 } else { 304 // It's a direct table reference - extract until whitespace or special char 305 StringBuilder tableName = new StringBuilder(); 306 for (int i = 0; i < afterOn.length(); i++) { 307 char c = afterOn.charAt(i); 308 if (Character.isWhitespace(c) || c == ')' || c == ',') { 309 break; 310 } 311 tableName.append(c); 312 } 313 if (tableName.length() > 0) { 314 // Parse the table reference 315 TGSqlParser subParser = new TGSqlParser(EDbVendor.dbvteradata); 316 subParser.sqltext = "SELECT * FROM " + tableName.toString() + " __npath_on"; 317 if (subParser.parse() == 0 && subParser.sqlstatements.size() > 0) { 318 gudusoft.gsqlparser.stmt.TSelectSqlStatement subSelect = 319 (gudusoft.gsqlparser.stmt.TSelectSqlStatement) subParser.sqlstatements.get(0); 320 if (subSelect.tables != null && subSelect.tables.size() > 0) { 321 TTable sourceTable = subSelect.tables.getTable(0); 322 addNPathSourceTable(sourceTable, psql); 323 } 324 } 325 } 326 } 327 } catch (Exception e) { 328 // Silently ignore parsing errors - the main query still works 329 } 330 } 331 332 /** 333 * Adds a source table from NPath ON clause to the current statement's table list. 334 */ 335 private void addNPathSourceTable(TTable sourceTable, TCustomSqlStatement psql) { 336 if (sourceTable == null || psql == null) { 337 return; 338 } 339 // Mark the table as coming from NPath ON clause 340 sourceTable.setNPathOnClauseTable(true); 341 // Add to the statement's table list 342 if (psql.tables == null) { 343 psql.tables = new TTableList(); 344 } 345 psql.tables.addTable(sourceTable); 346 } 347}