|
280 | 280 | </style> |
281 | 281 | </head> |
282 | 282 | <body> |
283 | | -<!-- vgi-landing-asset v4 --> |
| 283 | +<!-- vgi-landing-asset v5 --> |
284 | 284 |
|
285 | 285 | <a class="skip" href="#contents">Skip to contents</a> |
286 | 286 |
|
@@ -362,7 +362,7 @@ <h1 id="worker-name">—</h1> |
362 | 362 | import { |
363 | 363 | VgiClient, httpConnect, deserializeAttachOptionSpecs, deserializeSchema, |
364 | 364 | schemaToArgumentSpecs, TypeId |
365 | | -} from './vgi-client.js'; |
| 365 | +} from './vgi-client.js?v=5'; |
366 | 366 |
|
367 | 367 | // ── Catalog metadata, read straight off the VGI wire protocol ──────────────────── |
368 | 368 | // The page speaks the protocol the DuckDB extension speaks, through the JS |
@@ -417,7 +417,7 @@ <h1 id="worker-name">—</h1> |
417 | 417 | // JSON endpoint — the difference is where the bytes come from. |
418 | 418 | const columnCache = new Map(); |
419 | 419 | async function fetchColumns(catName, schema, table){ |
420 | | - const key = catName+' |
| 420 | + const key = JSON.stringify([catName, schema, table]); |
421 | 421 | if (columnCache.has(key)) return columnCache.get(key); |
422 | 422 | const cat = describe.catalogs.find(c=>c.name===catName); |
423 | 423 | if (!cat || !cat._aod) return {columns:[]}; |
@@ -480,13 +480,13 @@ <h1 id="worker-name">—</h1> |
480 | 480 | async function schemaEntry(aod, si){ |
481 | 481 | // A VGI worker commonly exposes its callable surface as declarative macros, |
482 | 482 | // so they fold into the same scalar/table buckets as functions. |
483 | | - const macros = (kind)=> client.schemaContentsMacros(aod, si.name, kind).catch(()=>[]); |
| 483 | + const macros = (kind)=> client.schemaContentsMacros(aod, si.path, kind).catch(()=>[]); |
484 | 484 | const [tablesRaw, viewsRaw, scalarFns, tableFns, aggFns, scalarMacros, tableMacros] = await Promise.all([ |
485 | | - client.schemaContentsTables(aod, si.name), |
486 | | - client.schemaContentsViews(aod, si.name), |
487 | | - client.schemaContentsFunctions(aod, si.name, 'SCALAR_FUNCTION'), |
488 | | - client.schemaContentsFunctions(aod, si.name, 'TABLE_FUNCTION'), |
489 | | - client.schemaContentsFunctions(aod, si.name, 'AGGREGATE_FUNCTION'), |
| 485 | + client.schemaContentsTables(aod, si.path), |
| 486 | + client.schemaContentsViews(aod, si.path), |
| 487 | + client.schemaContentsFunctions(aod, si.path, 'SCALAR_FUNCTION'), |
| 488 | + client.schemaContentsFunctions(aod, si.path, 'TABLE_FUNCTION'), |
| 489 | + client.schemaContentsFunctions(aod, si.path, 'AGGREGATE_FUNCTION'), |
490 | 490 | macros('SCALAR_MACRO'), |
491 | 491 | macros('TABLE_MACRO') |
492 | 492 | ]); |
@@ -522,7 +522,7 @@ <h1 id="worker-name">—</h1> |
522 | 522 | doc: m.comment || '', args: (m.parameters||[]).map(p=>({name:p})) |
523 | 523 | }))); |
524 | 524 | functions.sort((a,b)=> a.type===b.type ? byName(a,b) : (a.type<b.type?-1:1)); |
525 | | - const entry = {name: si.name, tables, views, functions}; |
| 525 | + const entry = {name: si.path.join('.'), path: si.path, tables, views, functions}; |
526 | 526 | if (si.comment) entry.doc = si.comment; |
527 | 527 | return entry; |
528 | 528 | } |
@@ -563,7 +563,7 @@ <h1 id="worker-name">—</h1> |
563 | 563 | cat.tags = mapTags(attach.tags || {}); |
564 | 564 |
|
565 | 565 | const schemaInfos = (await client.schemas(cat._aod)).slice() |
566 | | - .sort((a,b)=>a.name<b.name?-1:a.name>b.name?1:0); |
| 566 | + .sort((a,b)=>a.path.join('.').localeCompare(b.path.join('.'))); |
567 | 567 | cat.schemas = await Promise.all(schemaInfos.map(si=>schemaEntry(cat._aod, si))); |
568 | 568 | // `s.functions` holds both, so macros are counted apart rather than inflating |
569 | 569 | // the function count — vgi-open-meteo reads "13 functions · 6 macros", not |
@@ -754,7 +754,7 @@ <h1 id="worker-name">—</h1> |
754 | 754 | // unsupported version) has no schemas to draw an example from. |
755 | 755 | const s = c.schemas[0]; if (!s) return 'main.my_table'; |
756 | 756 | const t = (s.tables[0]||s.views[0]||{name:'my_table'}); |
757 | | - return s.name+'.'+t.name; |
| 757 | + return s.path.concat(t.name).map(n=>/^[a-z_][a-z0-9_]*$/i.test(n) ? n : '"'+n.replace(/"/g,'""')+'"').join('.'); |
758 | 758 | } |
759 | 759 | function connectCode(c, pinned, lang){ |
760 | 760 | const q=c.name, ex=exampleTable(c), P=attachParen(c,pinned); |
@@ -843,7 +843,7 @@ <h1 id="worker-name">—</h1> |
843 | 843 | function tableRow(catName, s, t, kind){ // kind: 'table' | 'view' |
844 | 844 | const iconName = kind==='view' ? 'view' : 'table'; |
845 | 845 | const kcls = kind==='view' ? ' view k-view' : ''; |
846 | | - return '<details class="tbl'+kcls+'" data-name="'+esc(t.name.toLowerCase())+'" data-kind="'+kind+'" data-catalog="'+esc(catName)+'" data-schema="'+esc(s.name)+'" data-table="'+esc(t.name)+'">'+ |
| 846 | + return '<details class="tbl'+kcls+'" data-name="'+esc(t.name.toLowerCase())+'" data-kind="'+kind+'" data-catalog="'+esc(catName)+'" data-schema="'+esc(JSON.stringify(s.path))+'" data-table="'+esc(t.name)+'">'+ |
847 | 847 | '<summary>'+icon('caret','tri')+icon(iconName,'row-icon')+ |
848 | 848 | '<span class="t-name">'+esc(t.name)+'</span><span class="t-meta">'+t.cols+' cols</span></summary>'+ |
849 | 849 | '<div class="t-body">'+ |
@@ -948,7 +948,7 @@ <h1 id="worker-name">—</h1> |
948 | 948 | if (!d.classList || !d.classList.contains('tbl') || !d.open || d.dataset.loaded) return; |
949 | 949 | d.dataset.loaded = '1'; |
950 | 950 | const body = d.querySelector('.cols'); |
951 | | - fetchColumns(d.dataset.catalog, d.dataset.schema, d.dataset.table).then(info=>{ |
| 951 | + fetchColumns(d.dataset.catalog, JSON.parse(d.dataset.schema), d.dataset.table).then(info=>{ |
952 | 952 | body.innerHTML = info.columns.map(col=> |
953 | 953 | '<div class="col-item"><div class="col"><span class="col-name">'+esc(col.name)+'</span><span class="col-type">'+esc(duckdbType(col.type))+'</span></div>'+ |
954 | 954 | (col.comment? '<div class="col-comment">'+esc(col.comment)+'</div>':'')+'</div>').join(''); |
|
0 commit comments