@@ -2902,4 +2902,127 @@ describe("CTE autocomplete", () => {
29022902 expect ( columns . map ( ( s ) => s . label ) ) . toContain ( "symbol" )
29032903 } )
29042904 } )
2905+
2906+ // ===========================================================================
2907+ // Column-based table ranking
2908+ // ===========================================================================
2909+ describe ( "column-based table ranking" , ( ) => {
2910+ it ( "boosts tables that contain all referenced columns" , ( ) => {
2911+ // "symbol" and "price" both exist in trades but not in orders or users
2912+ const sql = "SELECT symbol, price FROM "
2913+ const suggestions = provider . getSuggestions ( sql , sql . length )
2914+ const tables = suggestions . filter ( ( s ) => s . kind === SuggestionKind . Table )
2915+ const trades = tables . find ( ( s ) => s . label === "trades" )
2916+ const orders = tables . find ( ( s ) => s . label === "orders" )
2917+ const users = tables . find ( ( s ) => s . label === "users" )
2918+ expect ( trades ?. priority ) . toBe ( SuggestionPriority . High )
2919+ expect ( orders ?. priority ) . toBe ( SuggestionPriority . MediumLow )
2920+ expect ( users ?. priority ) . toBe ( SuggestionPriority . MediumLow )
2921+ } )
2922+
2923+ it ( "partially matching tables get Medium priority; no-match tables stay MediumLow" , ( ) => {
2924+ // "symbol" is in trades; "id" is in orders — each table has one of the two
2925+ const sql = "SELECT symbol, id FROM "
2926+ const suggestions = provider . getSuggestions ( sql , sql . length )
2927+ const tables = suggestions . filter ( ( s ) => s . kind === SuggestionKind . Table )
2928+ const trades = tables . find ( ( s ) => s . label === "trades" )
2929+ const orders = tables . find ( ( s ) => s . label === "orders" )
2930+ const users = tables . find ( ( s ) => s . label === "users" )
2931+ // partial match → Medium (boosted but not full match)
2932+ expect ( trades ?. priority ) . toBe ( SuggestionPriority . Medium )
2933+ expect ( orders ?. priority ) . toBe ( SuggestionPriority . Medium )
2934+ // no match → default
2935+ expect ( users ?. priority ) . toBe ( SuggestionPriority . MediumLow )
2936+ } )
2937+
2938+ it ( "columns from two tables: both partially-matching tables get Medium" , ( ) => {
2939+ // "symbol" and "price" only in trades; "status" only in orders; "name" only in users
2940+ // → trades and orders both partially match (2 and 1 out of 3); users has none
2941+ const sql = "SELECT symbol, price, status FROM "
2942+ const suggestions = provider . getSuggestions ( sql , sql . length )
2943+ const tables = suggestions . filter ( ( s ) => s . kind === SuggestionKind . Table )
2944+ const trades = tables . find ( ( s ) => s . label === "trades" )
2945+ const orders = tables . find ( ( s ) => s . label === "orders" )
2946+ const users = tables . find ( ( s ) => s . label === "users" )
2947+ expect ( trades ?. priority ) . toBe ( SuggestionPriority . Medium )
2948+ expect ( orders ?. priority ) . toBe ( SuggestionPriority . Medium )
2949+ expect ( users ?. priority ) . toBe ( SuggestionPriority . MediumLow )
2950+ } )
2951+
2952+ it ( "graceful fallback: no boost when no table has any referenced column" , ( ) => {
2953+ // "nonexistent_col" doesn't exist in any table
2954+ const sql = "SELECT nonexistent_col FROM "
2955+ const suggestions = provider . getSuggestions ( sql , sql . length )
2956+ const tables = suggestions . filter ( ( s ) => s . kind === SuggestionKind . Table )
2957+ for ( const t of tables ) {
2958+ expect ( t . priority ) . toBe ( SuggestionPriority . MediumLow )
2959+ }
2960+ } )
2961+
2962+ it ( "qualified references: the alias/qualifier is excluded but the column name is used" , ( ) => {
2963+ // "t1.symbol" → "symbol" is extracted; "t1" (alias qualifier) is not
2964+ const sql = "SELECT t1.symbol FROM "
2965+ const suggestions = provider . getSuggestions ( sql , sql . length )
2966+ const tables = suggestions . filter ( ( s ) => s . kind === SuggestionKind . Table )
2967+ const trades = tables . find ( ( s ) => s . label === "trades" )
2968+ const orders = tables . find ( ( s ) => s . label === "orders" )
2969+ // trades has "symbol" → boosted; orders does not
2970+ expect ( trades ?. priority ) . toBe ( SuggestionPriority . High )
2971+ expect ( orders ?. priority ) . toBe ( SuggestionPriority . MediumLow )
2972+ } )
2973+
2974+ it ( "qualified references from multiple aliases boost the correct tables" , ( ) => {
2975+ // c.symbol → symbol in trades; o.id → id in orders
2976+ const sql = "SELECT c.symbol, o.id FROM "
2977+ const suggestions = provider . getSuggestions ( sql , sql . length )
2978+ const tables = suggestions . filter ( ( s ) => s . kind === SuggestionKind . Table )
2979+ const trades = tables . find ( ( s ) => s . label === "trades" )
2980+ const orders = tables . find ( ( s ) => s . label === "orders" )
2981+ const users = tables . find ( ( s ) => s . label === "users" )
2982+ expect ( trades ?. priority ) . toBe ( SuggestionPriority . Medium ) // partial: symbol but not id
2983+ expect ( orders ?. priority ) . toBe ( SuggestionPriority . Medium ) // partial: id but not symbol
2984+ expect ( users ?. priority ) . toBe ( SuggestionPriority . MediumLow )
2985+ } )
2986+
2987+ it ( "function calls are excluded from column inference" , ( ) => {
2988+ // "count()" is a function call — should not influence ranking
2989+ const sql = "SELECT count() FROM "
2990+ const suggestions = provider . getSuggestions ( sql , sql . length )
2991+ const tables = suggestions . filter ( ( s ) => s . kind === SuggestionKind . Table )
2992+ for ( const t of tables ) {
2993+ expect ( t . priority ) . toBe ( SuggestionPriority . MediumLow )
2994+ }
2995+ } )
2996+
2997+ it ( "all tables remain in the suggestion list even when some are boosted" , ( ) => {
2998+ const sql = "SELECT symbol, price FROM "
2999+ const suggestions = provider . getSuggestions ( sql , sql . length )
3000+ const tableLabels = suggestions
3001+ . filter ( ( s ) => s . kind === SuggestionKind . Table )
3002+ . map ( ( s ) => s . label )
3003+ expect ( tableLabels ) . toContain ( "trades" )
3004+ expect ( tableLabels ) . toContain ( "orders" )
3005+ expect ( tableLabels ) . toContain ( "users" )
3006+ } )
3007+
3008+ it ( "boosts a single-column match correctly" , ( ) => {
3009+ // "status" only exists in orders
3010+ const sql = "SELECT status FROM "
3011+ const suggestions = provider . getSuggestions ( sql , sql . length )
3012+ const tables = suggestions . filter ( ( s ) => s . kind === SuggestionKind . Table )
3013+ const orders = tables . find ( ( s ) => s . label === "orders" )
3014+ const trades = tables . find ( ( s ) => s . label === "trades" )
3015+ expect ( orders ?. priority ) . toBe ( SuggestionPriority . High )
3016+ expect ( trades ?. priority ) . toBe ( SuggestionPriority . MediumLow )
3017+ } )
3018+
3019+ it ( "SELECT * FROM does not boost any table (no referenced columns)" , ( ) => {
3020+ const sql = "SELECT * FROM "
3021+ const suggestions = provider . getSuggestions ( sql , sql . length )
3022+ const tables = suggestions . filter ( ( s ) => s . kind === SuggestionKind . Table )
3023+ for ( const t of tables ) {
3024+ expect ( t . priority ) . toBe ( SuggestionPriority . MediumLow )
3025+ }
3026+ } )
3027+ } )
29053028} )
0 commit comments