001import java.io.*;
002import java.nio.charset.StandardCharsets;
003import java.util.*;
004
005import gudusoft.gsqlparser.*;
006import gudusoft.gsqlparser.resolver2.format.DisplayNameMode;
007
008import static gudusoft.gsqlparser.resolver2.format.DisplayNameMode.*;
009
010/**
011 * Standalone executable to compare RESOLVER (old) vs RESOLVER2 (new) results.
012 *
013 * Exit codes:
014 *    0: Both resolvers return the same result (or single resolver ran successfully)
015 *    1: Resolvers return different results
016 *    2: Parse error or file error
017 *
018 * Usage: java compareResolvers [options] <dbvendor> <sqlfile>
019 *
020 * Options:
021 *   -v, --verbose       Enable detailed output messages
022 *   -r1, --resolver1    Only run and print RESOLVER (old) result
023 *   -r2, --resolver2    Only run and print RESOLVER2 (new) result
024 *   -m, --mode <mode>   Set display name mode: DISPLAY, SQL_RENDER, or CANONICAL
025 *                       Default: SQL_RENDER for -r1, DISPLAY for -r2
026 *
027 * Display Name Modes:
028 *   DISPLAY    - Strip delimiters, preserve original case ([OrderID] -> OrderID)
029 *   SQL_RENDER - Preserve delimiters for valid SQL regeneration ([Order ID] -> [Order ID])
030 *   CANONICAL  - Apply vendor-specific case folding (Oracle: MyTable -> MYTABLE)
031 *
032 * Examples:
033 *   java compareResolvers oracle /path/to/query.sql
034 *   java compareResolvers -v mysql /path/to/query.sql
035 *   java compareResolvers -r1 oracle /path/to/query.sql         # RESOLVER with SQL_RENDER
036 *   java compareResolvers -r2 oracle /path/to/query.sql         # RESOLVER2 with DISPLAY
037 *   java compareResolvers -r2 -m SQL_RENDER oracle /path/to/query.sql  # RESOLVER2 with SQL_RENDER
038 */
039public class compareResolvers {
040
041    private static boolean verbose = false;
042    private static boolean onlyResolver1 = false;
043    private static boolean onlyResolver2 = false;
044    private static DisplayNameMode displayNameMode = null;  // null means use default based on resolver
045
046    public static void main(String[] args) {
047        int exitCode = run(args);
048        System.exit(exitCode);
049    }
050
051    /**
052     * Main entry point that returns an exit code.
053     * @param args command line arguments
054     * @return 0 for same results, 1 for different results, 2 for parse/file error
055     */
056    public static int run(String[] args) {
057        // Reset static flags (important for multiple calls in same JVM)
058        verbose = false;
059        onlyResolver1 = false;
060        onlyResolver2 = false;
061        displayNameMode = null;
062
063        // Parse arguments
064        List<String> positionalArgs = new ArrayList<>();
065        for (int i = 0; i < args.length; i++) {
066            String arg = args[i];
067            if (arg.equals("-v") || arg.equals("--verbose")) {
068                verbose = true;
069            } else if (arg.equals("-r1") || arg.equals("--resolver1")) {
070                onlyResolver1 = true;
071            } else if (arg.equals("-r2") || arg.equals("--resolver2")) {
072                onlyResolver2 = true;
073            } else if (arg.equals("-m") || arg.equals("--mode")) {
074                if (i + 1 >= args.length) {
075                    printError("Missing value for " + arg + " option");
076                    printUsage();
077                    return 2;
078                }
079                String modeStr = args[++i].toUpperCase();
080                displayNameMode = parseDisplayNameMode(modeStr);
081                if (displayNameMode == null) {
082                    printError("Invalid display name mode: " + modeStr);
083                    printError("Valid modes: DISPLAY, SQL_RENDER, CANONICAL");
084                    return 2;
085                }
086            } else if (arg.startsWith("-")) {
087                printError("Unknown option: " + arg);
088                printUsage();
089                return 2;
090            } else {
091                positionalArgs.add(arg);
092            }
093        }
094
095        // Validate mutually exclusive options
096        if (onlyResolver1 && onlyResolver2) {
097            printError("Options -r1 and -r2 are mutually exclusive");
098            printUsage();
099            return 2;
100        }
101
102        if (positionalArgs.size() < 2) {
103            printError("Missing required arguments");
104            printUsage();
105            return 2;
106        }
107
108        String dbVendorStr = positionalArgs.get(0);
109        String sqlFilePath = positionalArgs.get(1);
110
111        // Parse database vendor
112        EDbVendor dbVendor = getDbVendor(dbVendorStr);
113        if (dbVendor == null) {
114            printError("Unknown database vendor: " + dbVendorStr);
115            printSupportedVendors();
116            return 2;
117        }
118
119        return compareResolvers(dbVendor, sqlFilePath);
120    }
121
122    /**
123     * Compare table and column resolution results between RESOLVER and RESOLVER2.
124     *
125     * @param dbVendor The database vendor
126     * @param sqlFilePath The path to the input SQL file
127     * @return 2 for parse/file error, 0 for same results (or single resolver success), 1 for different results
128     */
129    public static int compareResolvers(EDbVendor dbVendor, String sqlFilePath) {
130        if (onlyResolver1) {
131            printVerbose(repeatChar('=', 80));
132            printVerbose("Running RESOLVER (old) only");
133            printVerbose("Database Vendor: " + dbVendor);
134            printVerbose("SQL File: " + sqlFilePath);
135            printVerbose(repeatChar('=', 80));
136            printVerbose("");
137        } else if (onlyResolver2) {
138            printVerbose(repeatChar('=', 80));
139            printVerbose("Running RESOLVER2 (new) only");
140            printVerbose("Database Vendor: " + dbVendor);
141            printVerbose("SQL File: " + sqlFilePath);
142            printVerbose(repeatChar('=', 80));
143            printVerbose("");
144        } else {
145            printVerbose(repeatChar('=', 80));
146            printVerbose("Comparing RESOLVER vs RESOLVER2");
147            printVerbose("Database Vendor: " + dbVendor);
148            printVerbose("SQL File: " + sqlFilePath);
149            printVerbose(repeatChar('=', 80));
150            printVerbose("");
151        }
152
153        // Read SQL file content
154        File sqlFile = new File(sqlFilePath);
155        if (!sqlFile.exists()) {
156            printError("SQL file does not exist: " + sqlFilePath);
157            return 2;
158        }
159
160        String sqlContent;
161        try {
162            sqlContent = new String(java.nio.file.Files.readAllBytes(
163                java.nio.file.Paths.get(sqlFilePath)), StandardCharsets.UTF_8);
164        } catch (IOException e) {
165            printError("Failed to read SQL file: " + e.getMessage());
166            return 2;
167        }
168
169        printVerbose("Input SQL:");
170        printVerbose(repeatChar('-', 40));
171        printVerbose(sqlContent);
172        printVerbose(repeatChar('-', 40));
173        printVerbose("");
174
175        // Create shared options for both resolvers
176        ResolverComparisonOptions options = new ResolverComparisonOptions();
177
178        // Single resolver mode: only run the requested resolver
179        if (onlyResolver1) {
180            ResolverResult resolver1Result = getResolver1Result(dbVendor, sqlContent, options);
181            if (resolver1Result.hasError) {
182                return 2;
183            }
184            System.out.println(resolver1Result.result);
185            return 0;
186        }
187
188        if (onlyResolver2) {
189            ResolverResult resolver2Result = getResolver2Result(dbVendor, sqlContent, options);
190            if (resolver2Result.hasError) {
191                return 2;
192            }
193            System.out.println(resolver2Result.result);
194            return 0;
195        }
196
197        // ========== RESOLVER (old) using TGetTableColumn ==========
198        ResolverResult resolver1Result = getResolver1Result(dbVendor, sqlContent, options);
199        if (resolver1Result.hasError) {
200            return 2;
201        }
202
203        // ========== RESOLVER2 (new) using TSQLResolver2ResultFormatter ==========
204        ResolverResult resolver2Result = getResolver2Result(dbVendor, sqlContent, options);
205        if (resolver2Result.hasError) {
206            return 2;
207        }
208
209        // ========== Compare results ==========
210        printVerbose(repeatChar('=', 80));
211        printVerbose("COMPARISON RESULT");
212        printVerbose(repeatChar('=', 80));
213        printVerbose("");
214
215        // Use semantic comparison (normalized tables and fields) instead of raw string comparison
216        // This handles formatting differences like backticks in RESOLVER vs no backticks in RESOLVER2
217        if (areResultsSemanticallyEqual(resolver1Result.result, resolver2Result.result)) {
218            printVerbose("* RESULTS ARE THE SAME");
219            printVerbose("");
220            printVerbose("Result:");
221            printVerbose(repeatChar('-', 40));
222            printVerbose(resolver1Result.result);
223            return 0;
224        } else {
225            printVerbose("X RESULTS ARE DIFFERENT");
226            printVerbose("");
227
228            // Show differences
229            showDifferences(resolver1Result.result, resolver2Result.result);
230
231            printVerbose("");
232            printVerbose("RESOLVER (TGetTableColumn) output:");
233            printVerbose(repeatChar('-', 40));
234            printVerbose(resolver1Result.result);
235            printVerbose("");
236            printVerbose("RESOLVER2 (TSQLResolver2ResultFormatter) output:");
237            printVerbose(repeatChar('-', 40));
238            printVerbose(resolver2Result.result);
239            return 1;
240        }
241    }
242
243    /**
244     * Check if two results are semantically equal (same tables and fields after normalization).
245     * This handles formatting differences like backticks in RESOLVER vs no backticks in RESOLVER2.
246     */
247    private static boolean areResultsSemanticallyEqual(String result1, String result2) {
248        // Parse both results into normalized tables and fields
249        Set<String> tables1 = extractSection(result1, "Tables:");
250        Set<String> tables2 = extractSection(result2, "Tables:");
251        Set<String> fields1 = extractSection(result1, "Fields:");
252        Set<String> fields2 = extractSection(result2, "Fields:");
253
254        // Compare normalized sets
255        return tables1.equals(tables2) && fields1.equals(fields2);
256    }
257
258    /**
259     * Result holder for resolver output.
260     */
261    private static class ResolverResult {
262        String result;
263        boolean hasError;
264
265        ResolverResult(String result, boolean hasError) {
266            this.result = result;
267            this.hasError = hasError;
268        }
269    }
270
271    /**
272     * Options for resolver comparison.
273     */
274    private static class ResolverComparisonOptions {
275        boolean showTableEffect = false;
276        boolean showColumnLocation = false;
277        boolean listStarColumn = true;
278        boolean showDatatype = false;
279        boolean linkOrphanColumnToFirstTable = false;
280        boolean showCTE = false;
281        boolean showColumnsOfCTE = false;
282    }
283
284    /**
285     * Get table/column result using RESOLVER (old) with TGetTableColumn.
286     */
287    private static ResolverResult getResolver1Result(EDbVendor dbVendor, String sqlContent, ResolverComparisonOptions options) {
288        printVerbose("Running RESOLVER (TGetTableColumn)...");
289
290        gudusoft.gsqlparser.util.TGetTableColumn getTableColumn =
291            new gudusoft.gsqlparser.util.TGetTableColumn(dbVendor);
292        getTableColumn.setResolverType(EResolverType.RESOLVER);
293        getTableColumn.isConsole = false;
294        getTableColumn.showDetail = false;
295        getTableColumn.showSummary = true;
296
297        // Apply shared options
298        getTableColumn.showTableEffect = options.showTableEffect;
299        getTableColumn.showColumnLocation = options.showColumnLocation;
300        getTableColumn.listStarColumn = options.listStarColumn;
301        getTableColumn.showDatatype = options.showDatatype;
302        getTableColumn.linkOrphanColumnToFirstTable = options.linkOrphanColumnToFirstTable;
303        getTableColumn.showCTE = options.showCTE;
304        getTableColumn.showColumnsOfCTE = options.showColumnsOfCTE;
305
306        // Set display name mode (default SQL_RENDER for RESOLVER)
307        DisplayNameMode mode = displayNameMode != null ? displayNameMode : SQL_RENDER;
308        getTableColumn.setDisplayNameMode(mode);
309        printVerbose("Display name mode: " + mode);
310
311        try {
312            getTableColumn.runText(sqlContent);
313        } catch (RuntimeException e) {
314            printError("RESOLVER parse failed: " + e.getMessage());
315            return new ResolverResult("", true);
316        }
317
318        String result = getTableColumn.outList.toString();
319        printVerbose("RESOLVER completed.");
320        printVerbose("");
321        return new ResolverResult(result.trim(), false);
322    }
323
324    /**
325     * Get table/column result using RESOLVER2 (new) with TSQLResolver2ResultFormatter.
326     */
327    private static ResolverResult getResolver2Result(EDbVendor dbVendor, String sqlContent, ResolverComparisonOptions options) {
328        printVerbose("Running RESOLVER2 (TSQLResolver2ResultFormatter)...");
329
330        TGSqlParser parser = new TGSqlParser(dbVendor);
331        parser.setResolverType(EResolverType.RESOLVER2);
332        parser.sqltext = sqlContent;
333
334        int ret = parser.parse();
335        if (ret != 0) {
336            printError("RESOLVER2 parse failed: " + parser.getErrormessage());
337            return new ResolverResult("", true);
338        }
339
340        // Get resolver2 and format results
341        gudusoft.gsqlparser.resolver2.TSQLResolver2 resolver = parser.getResolver2();
342        if (resolver == null) {
343            printError("Resolver2 not available after parsing");
344            return new ResolverResult("", true);
345        }
346
347        gudusoft.gsqlparser.resolver2.TSQLResolver2ResultFormatter formatter =
348            new gudusoft.gsqlparser.resolver2.TSQLResolver2ResultFormatter(resolver);
349
350        // Apply shared options
351        formatter.setShowTableEffect(options.showTableEffect);
352        formatter.setShowColumnLocation(options.showColumnLocation);
353        formatter.setListStarColumn(options.listStarColumn);
354        formatter.setShowDatatype(options.showDatatype);
355        formatter.setLinkOrphanColumnToFirstTable(options.linkOrphanColumnToFirstTable);
356        formatter.setShowCTE(options.showCTE);
357        formatter.setShowColumnsOfCTE(options.showColumnsOfCTE);
358
359        // Set display name mode (default DISPLAY for RESOLVER2)
360        DisplayNameMode mode = displayNameMode != null ? displayNameMode : DISPLAY;
361        formatter.setDisplayNameMode(mode);
362        printVerbose("Display name mode: " + mode);
363
364        String result = formatter.format();
365
366        printVerbose("RESOLVER2 completed.");
367        printVerbose("");
368        return new ResolverResult(result.trim(), false);
369    }
370
371    /**
372     * Show detailed differences between two results.
373     */
374    private static void showDifferences(String result1, String result2) {
375        printVerbose("Differences:");
376        printVerbose(repeatChar('-', 40));
377
378        // Parse both results into tables and fields
379        Set<String> tables1 = extractSection(result1, "Tables:");
380        Set<String> tables2 = extractSection(result2, "Tables:");
381        Set<String> fields1 = extractSection(result1, "Fields:");
382        Set<String> fields2 = extractSection(result2, "Fields:");
383
384        // Compare tables
385        Set<String> tablesOnlyIn1 = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
386        tablesOnlyIn1.addAll(tables1);
387        tablesOnlyIn1.removeAll(tables2);
388
389        Set<String> tablesOnlyIn2 = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
390        tablesOnlyIn2.addAll(tables2);
391        tablesOnlyIn2.removeAll(tables1);
392
393        if (!tablesOnlyIn1.isEmpty()) {
394            printVerbose("Tables only in RESOLVER: " + tablesOnlyIn1);
395        }
396        if (!tablesOnlyIn2.isEmpty()) {
397            printVerbose("Tables only in RESOLVER2: " + tablesOnlyIn2);
398        }
399
400        // Compare fields
401        Set<String> fieldsOnlyIn1 = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
402        fieldsOnlyIn1.addAll(fields1);
403        fieldsOnlyIn1.removeAll(fields2);
404
405        Set<String> fieldsOnlyIn2 = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
406        fieldsOnlyIn2.addAll(fields2);
407        fieldsOnlyIn2.removeAll(fields1);
408
409        if (!fieldsOnlyIn1.isEmpty()) {
410            printVerbose("Fields only in RESOLVER: " + fieldsOnlyIn1);
411        }
412        if (!fieldsOnlyIn2.isEmpty()) {
413            printVerbose("Fields only in RESOLVER2: " + fieldsOnlyIn2);
414        }
415
416        // Show common items
417        Set<String> commonTables = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
418        commonTables.addAll(tables1);
419        commonTables.retainAll(tables2);
420
421        Set<String> commonFields = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
422        commonFields.addAll(fields1);
423        commonFields.retainAll(fields2);
424
425        if (!commonTables.isEmpty() || !commonFields.isEmpty()) {
426            printVerbose("");
427            printVerbose("Common items:");
428            if (!commonTables.isEmpty()) {
429                printVerbose("  Tables: " + commonTables);
430            }
431            if (!commonFields.isEmpty()) {
432                printVerbose("  Fields: " + commonFields);
433            }
434        }
435    }
436
437    /**
438     * Extract a section (Tables: or Fields:) from the result string.
439     * Normalizes identifiers by stripping SQL delimiters (backticks, quotes).
440     */
441    private static Set<String> extractSection(String result, String sectionName) {
442        Set<String> items = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
443        String[] lines = result.split("\n");
444        boolean inSection = false;
445
446        for (String line : lines) {
447            line = line.trim();
448            if (line.isEmpty()) continue;
449
450            if (line.equalsIgnoreCase(sectionName.trim().replace(":", ""))) {
451                inSection = true;
452                continue;
453            }
454            if (line.toLowerCase().startsWith("tables:")) {
455                inSection = sectionName.toLowerCase().contains("tables");
456                continue;
457            }
458            if (line.toLowerCase().startsWith("fields:")) {
459                inSection = sectionName.toLowerCase().contains("fields");
460                continue;
461            }
462            if (line.toLowerCase().startsWith("ctes:")) {
463                inSection = false;
464                continue;
465            }
466
467            if (inSection && !line.isEmpty()) {
468                // Normalize the identifier by stripping SQL delimiters
469                items.add(normalizeIdentifier(line));
470            }
471        }
472
473        return items;
474    }
475
476    /**
477     * Normalize an identifier by stripping SQL delimiters (backticks, double quotes).
478     * This allows fair comparison between RESOLVER (preserves delimiters) and
479     * RESOLVER2 (strips delimiters).
480     *
481     * Examples:
482     *   `schema`.`table`.column -> schema.table.column
483     *   "schema"."table"."column" -> schema.table.column
484     *   schema.table.column -> schema.table.column (unchanged)
485     */
486    private static String normalizeIdentifier(String identifier) {
487        if (identifier == null || identifier.isEmpty()) {
488            return identifier;
489        }
490
491        StringBuilder result = new StringBuilder();
492        boolean inBacktick = false;
493        boolean inDoubleQuote = false;
494
495        for (int i = 0; i < identifier.length(); i++) {
496            char c = identifier.charAt(i);
497
498            if (c == '`' && !inDoubleQuote) {
499                // Toggle backtick state, don't add the backtick to result
500                inBacktick = !inBacktick;
501            } else if (c == '"' && !inBacktick) {
502                // Toggle double quote state, don't add the quote to result
503                inDoubleQuote = !inDoubleQuote;
504            } else {
505                result.append(c);
506            }
507        }
508
509        // Trim leading/trailing spaces that may have been inside quotes
510        return result.toString().trim();
511    }
512
513    /**
514     * Helper method to repeat a character (Java 8 compatible).
515     */
516    private static String repeatChar(char c, int count) {
517        StringBuilder sb = new StringBuilder(count);
518        for (int i = 0; i < count; i++) {
519            sb.append(c);
520        }
521        return sb.toString();
522    }
523
524    /**
525     * Print message only if verbose mode is enabled.
526     */
527    private static void printVerbose(String message) {
528        if (verbose) {
529            System.out.println(message);
530        }
531    }
532
533    /**
534     * Print error message (always printed).
535     */
536    private static void printError(String message) {
537        System.err.println("ERROR: " + message);
538    }
539
540    /**
541     * Print usage information.
542     */
543    private static void printUsage() {
544        System.err.println();
545        System.err.println("Usage: java compareResolvers [options] <dbvendor> <sqlfile>");
546        System.err.println();
547        System.err.println("Options:");
548        System.err.println("  -v, --verbose       Enable detailed output messages");
549        System.err.println("  -r1, --resolver1    Only run and print RESOLVER (old) result");
550        System.err.println("  -r2, --resolver2    Only run and print RESOLVER2 (new) result");
551        System.err.println("  -m, --mode <mode>   Set display name mode: DISPLAY, SQL_RENDER, or CANONICAL");
552        System.err.println("                      Default: SQL_RENDER for -r1, DISPLAY for -r2");
553        System.err.println();
554        System.err.println("Display Name Modes:");
555        System.err.println("  DISPLAY    - Strip delimiters, preserve original case");
556        System.err.println("  SQL_RENDER - Preserve delimiters for valid SQL regeneration");
557        System.err.println("  CANONICAL  - Apply vendor-specific case folding");
558        System.err.println();
559        System.err.println("Exit codes:");
560        System.err.println("   0: Both resolvers return the same result (or single resolver success)");
561        System.err.println("   1: Resolvers return different results");
562        System.err.println("   2: Parse error or file error");
563        System.err.println();
564        System.err.println("Examples:");
565        System.err.println("  java compareResolvers oracle /path/to/query.sql");
566        System.err.println("  java compareResolvers -v mysql /path/to/query.sql");
567        System.err.println("  java compareResolvers -r1 oracle /path/to/query.sql");
568        System.err.println("  java compareResolvers -r2 oracle /path/to/query.sql");
569        System.err.println("  java compareResolvers -r2 -m SQL_RENDER oracle /path/to/query.sql");
570    }
571
572    /**
573     * Parse display name mode from string.
574     * @return DisplayNameMode or null if invalid
575     */
576    private static DisplayNameMode parseDisplayNameMode(String modeStr) {
577        if (modeStr == null) return null;
578        switch (modeStr.toUpperCase()) {
579            case "DISPLAY": return DISPLAY;
580            case "SQL_RENDER": return SQL_RENDER;
581            case "CANONICAL": return CANONICAL;
582            default: return null;
583        }
584    }
585
586    /**
587     * Print supported database vendors.
588     */
589    private static void printSupportedVendors() {
590        System.err.println();
591        System.err.println("Supported database vendors:");
592        System.err.println("  oracle, mysql, mssql, postgresql, db2, teradata,");
593        System.err.println("  informix, sybase, netezza, greenplum, hive, redshift,");
594        System.err.println("  snowflake, bigquery, athena, sparksql, vertica, impala,");
595        System.err.println("  couchbase, dax, mdx, gaussdb, openedge, etc.");
596    }
597
598    /**
599     * Convert database vendor string to EDbVendor enum.
600     */
601    private static EDbVendor getDbVendor(String vendorStr) {
602        String vendor = vendorStr.toLowerCase().trim();
603        switch (vendor) {
604            case "oracle": return EDbVendor.dbvoracle;
605            case "mysql": return EDbVendor.dbvmysql;
606            case "mssql":
607            case "sqlserver": return EDbVendor.dbvmssql;
608            case "postgresql":
609            case "postgres": return EDbVendor.dbvpostgresql;
610            case "db2": return EDbVendor.dbvdb2;
611            case "teradata": return EDbVendor.dbvteradata;
612            case "informix": return EDbVendor.dbvinformix;
613            case "sybase": return EDbVendor.dbvsybase;
614            case "netezza": return EDbVendor.dbvnetezza;
615            case "greenplum": return EDbVendor.dbvgreenplum;
616            case "hive": return EDbVendor.dbvhive;
617            case "redshift": return EDbVendor.dbvredshift;
618            case "snowflake": return EDbVendor.dbvsnowflake;
619            case "bigquery": return EDbVendor.dbvbigquery;
620            case "athena": return EDbVendor.dbvathena;
621            case "sparksql":
622            case "spark": return EDbVendor.dbvsparksql;
623            case "vertica": return EDbVendor.dbvvertica;
624            case "impala": return EDbVendor.dbvimpala;
625            case "couchbase": return EDbVendor.dbvcouchbase;
626            case "dax": return EDbVendor.dbvdax;
627            case "mdx": return EDbVendor.dbvmdx;
628            case "gaussdb": return EDbVendor.dbvgaussdb;
629            case "openedge": return EDbVendor.dbvopenedge;
630            case "databricks": return EDbVendor.dbvdatabricks;
631            case "hana": return EDbVendor.dbvhana;
632            default: return null;
633        }
634    }
635}