Skip to content

Commit 41654ce

Browse files
Support pg vector as pg_knn for postgres source (#57)
* # This is a combination of 3 commits. # This is the 1st commit message: support pg vector support pg vector example update example hard-code k fix CLI remove fallback case fix fallback remove hnsw demo yaml fix k format property support subquery update shared logic use subquery fix sub query path # This is the commit message #2: support multi type of query # This is the commit message #3: add tests * support pg vector support pg vector example update example hard-code k fix CLI remove fallback case fix fallback remove hnsw demo yaml fix k format property support subquery update shared logic use subquery fix sub query path support multi type of query add tests test updates test updates simplify and add tests cleanup limit k update pipelines bug fix add conversion no cast * address comments * stick to 64 * revert lance * Revert "revert lance" This reverts commit 75b8c6a.
1 parent e2645d3 commit 41654ce

19 files changed

Lines changed: 2149 additions & 251 deletions

crates/cli/src/main.rs

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,10 @@ use object_store::http::HttpBuilder;
1818
use serde::Deserialize;
1919
use skardi::sources::providers::lance::fts_table_function::register_lance_fts_udtf;
2020
use skardi::sources::providers::lance::knn_table_function::register_lance_knn_udtf;
21+
use skardi::sources::providers::sqlx::register_pg_knn_udtf;
2122
use skardi::sources::providers::{
22-
iceberg::register_iceberg_table, lance::register_lance_table, mongo::register_mongo_tables,
23-
mysql::register_mysql_tables, sqlite::register_sqlite_tables,
23+
DatasetRegistry, iceberg::register_iceberg_table, lance::register_lance_table,
24+
mongo::register_mongo_tables, mysql::register_mysql_tables, sqlite::register_sqlite_tables,
2425
sqlx::postgres::register_postgres_tables,
2526
};
2627
use std::collections::HashMap;
@@ -29,9 +30,6 @@ use std::path::{Path, PathBuf};
2930
use std::sync::{Arc, RwLock};
3031
use url::Url;
3132

32-
/// Shared registry mapping table names to Lance datasets, used by the `lance_knn` UDTF.
33-
type DatasetRegistry = Arc<RwLock<HashMap<String, Arc<Dataset>>>>;
34-
3533
#[derive(Parser)]
3634
#[command(name = "skardi")]
3735
#[command(about = "CLI tool for managing Skardi pipelines and data sources", long_about = None)]
@@ -278,7 +276,10 @@ impl UrlTableFactory for SkardiUrlTableFactory {
278276
.unwrap_or(url)
279277
.to_string();
280278
if let Ok(mut reg) = self.dataset_registry.write() {
281-
reg.insert(table_name, Arc::clone(&dataset_arc));
279+
reg.insert(
280+
table_name,
281+
skardi::sources::providers::DatasetEntry::Lance(Arc::clone(&dataset_arc)),
282+
);
282283
}
283284

284285
let provider: Arc<dyn TableProvider> = dataset_arc;
@@ -291,7 +292,7 @@ impl UrlTableFactory for SkardiUrlTableFactory {
291292
}
292293

293294
/// Create a new SessionContext with custom URL table support (built-in files + Lance)
294-
/// and the `lance_knn` UDTF registered. Returns the context and the shared dataset registry.
295+
/// and the `lance_knn` / `pg_knn` UDTFs registered.
295296
fn new_session_context() -> (SessionContext, DatasetRegistry) {
296297
let dataset_registry: DatasetRegistry = Arc::new(RwLock::new(HashMap::new()));
297298
let session_store = SessionStore::new();
@@ -317,9 +318,10 @@ fn new_session_context() -> (SessionContext, DatasetRegistry) {
317318

318319
factory.session_store().with_state(ctx.state_weak_ref());
319320

320-
// Register the lance_knn and lance_fts table functions
321+
// Register the lance_knn, lance_fts, and pg_knn table functions, all sharing one registry
321322
register_lance_knn_udtf(&ctx, Arc::clone(&dataset_registry));
322323
register_lance_fts_udtf(&ctx, Arc::clone(&dataset_registry));
324+
register_pg_knn_udtf(&ctx, Arc::clone(&dataset_registry));
323325

324326
(ctx, dataset_registry)
325327
}
@@ -503,6 +505,7 @@ async fn register_source(
503505
conn_str,
504506
source.options.as_ref(),
505507
false,
508+
Some(dataset_registry),
506509
)
507510
.await
508511
.with_context(|| format!("Failed to register Postgres '{}'", source.name))?;

crates/server/src/config.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -774,12 +774,14 @@ async fn register_data_source(
774774
);
775775

776776
// Register PostgreSQL table using the sqlx-based provider
777+
let pg_knn_registry = optimizer_registry.map(|r| r.pg_knn_pools());
777778
skardi::sources::providers::sqlx::postgres::register_postgres_tables(
778779
session_ctx,
779780
&source.name,
780781
connection_string,
781782
source.options.as_ref(),
782783
source.access_mode.is_read_write(),
784+
pg_knn_registry.as_ref(),
783785
)
784786
.await
785787
.map_err(|e| {

crates/server/src/optimizer_registry.rs

Lines changed: 37 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ use anyhow::Result;
1212
use datafusion::prelude::SessionContext;
1313
use lance::dataset::Dataset;
1414
use skardi::sources::providers::lance::{register_lance_fts_udtf, register_lance_knn_udtf};
15+
use skardi::sources::providers::sqlx::register_pg_knn_udtf;
16+
use skardi::sources::providers::{DatasetEntry, DatasetRegistry};
1517
use std::collections::{HashMap, HashSet};
1618
use std::sync::{Arc, RwLock};
1719

@@ -24,16 +26,17 @@ use crate::config::{DataSource, DataSourceType};
2426
/// 2. **Dataset Store** - Maintains references to datasets needed by table functions
2527
/// 3. **Lifecycle Coordinator** - Ensures datasets are available when functions need them
2628
pub struct OptimizerRegistry {
27-
/// Lance datasets indexed by table name
28-
/// Used by lance_knn table function to access datasets
29-
lance_datasets: Arc<RwLock<HashMap<String, Arc<Dataset>>>>,
29+
/// Unified dataset registry indexed by table name.
30+
/// Stores both Lance datasets and Postgres entries, used by
31+
/// lance_knn, lance_fts, and pg_knn table functions.
32+
dataset_registry: DatasetRegistry,
3033
}
3134

3235
impl OptimizerRegistry {
3336
/// Create a new registry
3437
pub fn new() -> Self {
3538
Self {
36-
lance_datasets: Arc::new(RwLock::new(HashMap::new())),
39+
dataset_registry: Arc::new(RwLock::new(HashMap::new())),
3740
}
3841
}
3942

@@ -81,9 +84,9 @@ impl OptimizerRegistry {
8184
self.register_lance_functions(ctx)?;
8285
}
8386

84-
// Future: Register Postgres-specific UDFs
87+
// Register Postgres-specific table functions
8588
if source_types.contains(&DataSourceType::Postgres) {
86-
self.register_postgres_udfs(ctx)?;
89+
self.register_postgres_functions(ctx)?;
8790
}
8891

8992
Ok(())
@@ -95,46 +98,54 @@ impl OptimizerRegistry {
9598
fn register_lance_functions(&self, ctx: &mut SessionContext) -> Result<()> {
9699
tracing::info!("Registering Lance table functions");
97100

98-
// Register lance_knn table function
99-
register_lance_knn_udtf(ctx, self.lance_datasets());
101+
register_lance_knn_udtf(ctx, self.datasets());
100102
tracing::info!("✓ Registered lance_knn table function");
101103

102-
// Register lance_fts table function
103-
register_lance_fts_udtf(ctx, self.lance_datasets());
104+
register_lance_fts_udtf(ctx, self.datasets());
104105
tracing::info!("✓ Registered lance_fts table function");
105106

106107
Ok(())
107108
}
108109

109-
/// Register Postgres-specific UDFs (placeholder for future)
110-
fn register_postgres_udfs(&self, _ctx: &mut SessionContext) -> Result<()> {
111-
tracing::debug!("Postgres UDFs not yet implemented");
110+
/// Register Postgres-specific table functions.
111+
fn register_postgres_functions(&self, ctx: &mut SessionContext) -> Result<()> {
112+
tracing::info!("Registering pg_knn table function");
113+
register_pg_knn_udtf(ctx, self.datasets());
114+
tracing::info!("✓ Registered pg_knn table function");
112115
Ok(())
113116
}
114117

115-
// === Lance Dataset Management ===
118+
// === Dataset Management ===
116119

117120
/// Store a Lance dataset in the registry
118-
///
119-
/// Called when registering a Lance table with DataFusion.
120-
/// The lance_knn table function uses this to look up datasets by name.
121121
pub fn register_lance_dataset(&self, table_name: &str, dataset: Arc<Dataset>) {
122-
let mut datasets = self.lance_datasets.write().unwrap();
123-
datasets.insert(table_name.to_string(), dataset);
122+
let mut reg = self.dataset_registry.write().unwrap();
123+
reg.insert(table_name.to_string(), DatasetEntry::Lance(dataset));
124124
tracing::debug!("Registered Lance dataset '{}' in registry", table_name);
125125
}
126126

127127
/// Get a Lance dataset by table name
128128
pub fn get_lance_dataset(&self, table_name: &str) -> Option<Arc<Dataset>> {
129-
let datasets = self.lance_datasets.read().unwrap();
130-
datasets.get(table_name).cloned()
129+
let reg = self.dataset_registry.read().unwrap();
130+
reg.get(table_name).and_then(|e| match e {
131+
DatasetEntry::Lance(ds) => Some(Arc::clone(ds)),
132+
_ => None,
133+
})
131134
}
132135

133-
/// Get a clone of the Lance datasets map
134-
///
135-
/// Returns an `Arc<RwLock<>>` that can be shared with table functions
136-
pub fn lance_datasets(&self) -> Arc<RwLock<HashMap<String, Arc<Dataset>>>> {
137-
Arc::clone(&self.lance_datasets)
136+
/// Get a clone of the unified dataset registry to share with table functions.
137+
pub fn datasets(&self) -> DatasetRegistry {
138+
Arc::clone(&self.dataset_registry)
139+
}
140+
141+
/// Alias for `datasets()` — used when passing to `register_postgres_tables`.
142+
pub fn pg_knn_pools(&self) -> DatasetRegistry {
143+
self.datasets()
144+
}
145+
146+
/// Alias for `datasets()` — used when passing to `register_lance_table`.
147+
pub fn lance_datasets(&self) -> DatasetRegistry {
148+
self.datasets()
138149
}
139150
}
140151

0 commit comments

Comments
 (0)