Skip to content

Commit 63a2747

Browse files
authored
Merge pull request #52 from PostHog/fix/information-schema-ducklake-views
Fix information_schema views for DuckLake mode
2 parents 8de3845 + 2604ad5 commit 63a2747

File tree

4 files changed

+43
-35
lines changed

4 files changed

+43
-35
lines changed

server/catalog.go

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -318,19 +318,16 @@ func initPgCatalog(db *sql.DB) error {
318318

319319
// initInformationSchema creates the column metadata table and information_schema wrapper views.
320320
// This enables accurate type information (VARCHAR lengths, NUMERIC precision) in information_schema.
321-
// When duckLakeMode is true, the views query from ducklake.information_schema instead of the
322-
// local information_schema, since DuckLake is set as the default catalog.
321+
// Views are created in memory.main (before USE ducklake) and query from unqualified information_schema,
322+
// which resolves to the default catalog's information_schema at query time.
323323
func initInformationSchema(db *sql.DB, duckLakeMode bool) error {
324-
// Determine the source information_schema based on mode
325-
// In DuckLake mode, we need to query ducklake.information_schema to see DuckLake tables
326-
// In non-DuckLake mode, we query the local information_schema
324+
// Use just "information_schema" without catalog prefix
325+
// Views are created in memory.main (before USE ducklake) and query from information_schema
326+
// which resolves to the current default catalog's information_schema at query time
327327
infoSchemaPrefix := "information_schema"
328-
if duckLakeMode {
329-
infoSchemaPrefix = "ducklake.information_schema"
330-
}
331328

332329
// Create metadata table to store column type information that DuckDB doesn't preserve
333-
// Table is created in main schema of current database
330+
// Table is created in main schema (which is memory.main before USE ducklake)
334331
metadataTableSQL := `
335332
CREATE TABLE IF NOT EXISTS main.__duckgres_column_metadata (
336333
table_schema VARCHAR NOT NULL,
@@ -351,7 +348,7 @@ func initInformationSchema(db *sql.DB, duckLakeMode bool) error {
351348
// Transforms DuckDB type names to PostgreSQL-compatible names
352349
// Maps: VARCHAR->text, BOOLEAN->boolean, INTEGER->integer, BIGINT->bigint,
353350
// TIMESTAMP->timestamp without time zone, DECIMAL->numeric, etc.
354-
// Views are created in main schema of current database
351+
// Views are created in main schema (which is memory.main before USE ducklake)
355352
columnsViewSQL := `
356353
CREATE OR REPLACE VIEW main.information_schema_columns_compat AS
357354
SELECT

server/server.go

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -280,7 +280,7 @@ func (s *Server) createDBConnection(username string) (*sql.DB, error) {
280280
// Continue anyway - basic queries will still work
281281
}
282282

283-
// Attach DuckLake catalog if configured
283+
// Attach DuckLake catalog if configured (but don't set as default yet)
284284
duckLakeMode := false
285285
if err := s.attachDuckLake(db); err != nil {
286286
// If DuckLake was explicitly configured, fail the connection.
@@ -295,13 +295,22 @@ func (s *Server) createDBConnection(username string) (*sql.DB, error) {
295295
duckLakeMode = true
296296
}
297297

298-
// Initialize information_schema compatibility views
299-
// Must be done AFTER attaching DuckLake so views can reference ducklake.information_schema
298+
// Initialize information_schema compatibility views in memory.main
299+
// Must be done AFTER attaching DuckLake (so views can reference ducklake.information_schema)
300+
// but BEFORE setting DuckLake as default (so views are created in memory.main, not ducklake.main)
300301
if err := initInformationSchema(db, duckLakeMode); err != nil {
301302
log.Printf("Warning: failed to initialize information_schema for user %q: %v", username, err)
302303
// Continue anyway - basic queries will still work
303304
}
304305

306+
// Now set DuckLake as the default catalog so all user queries use it
307+
if duckLakeMode {
308+
if err := setDuckLakeDefault(db); err != nil {
309+
db.Close()
310+
return nil, fmt.Errorf("failed to set DuckLake as default: %w", err)
311+
}
312+
}
313+
305314
return db, nil
306315
}
307316

@@ -333,7 +342,8 @@ func (s *Server) loadExtensions(db *sql.DB) error {
333342
return lastErr
334343
}
335344

336-
// attachDuckLake attaches a DuckLake catalog if configured
345+
// attachDuckLake attaches a DuckLake catalog if configured (but does NOT set it as default).
346+
// Call setDuckLakeDefault after creating per-connection views in memory.main.
337347
func (s *Server) attachDuckLake(db *sql.DB) error {
338348
if s.cfg.DuckLake.MetadataStore == "" {
339349
return nil // DuckLake not configured
@@ -355,10 +365,7 @@ func (s *Server) attachDuckLake(db *sql.DB) error {
355365
var count int
356366
err := db.QueryRow("SELECT COUNT(*) FROM duckdb_databases() WHERE database_name = 'ducklake'").Scan(&count)
357367
if err == nil && count > 0 {
358-
// Already attached, just set as default
359-
if _, err := db.Exec("USE ducklake"); err != nil {
360-
return fmt.Errorf("failed to set DuckLake as default catalog: %w", err)
361-
}
368+
// Already attached
362369
return nil
363370
}
364371

@@ -398,13 +405,17 @@ func (s *Server) attachDuckLake(db *sql.DB) error {
398405
return fmt.Errorf("failed to attach DuckLake: %w", err)
399406
}
400407

401-
// Set DuckLake as the default catalog so all queries use it
402-
// See: https://duckdb.org/docs/stable/core_extensions/ducklake#usage
408+
log.Printf("Attached DuckLake catalog successfully")
409+
return nil
410+
}
411+
412+
// setDuckLakeDefault sets the DuckLake catalog as the default so all queries use it.
413+
// This should be called AFTER creating per-connection views in memory.main.
414+
func setDuckLakeDefault(db *sql.DB) error {
403415
if _, err := db.Exec("USE ducklake"); err != nil {
404416
return fmt.Errorf("failed to set DuckLake as default catalog: %w", err)
405417
}
406-
407-
log.Printf("Attached DuckLake catalog successfully and set as default")
418+
log.Printf("Set DuckLake as default catalog")
408419
return nil
409420
}
410421

transpiler/transform/information_schema.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -60,17 +60,17 @@ func (t *InformationSchemaTransform) walkAndTransform(node *pg_query.Node, chang
6060

6161
switch n := node.Node.(type) {
6262
case *pg_query.Node_RangeVar:
63-
// Table references: information_schema.columns -> information_schema_columns_compat
64-
// Views are created in the main schema of the current database
63+
// Table references: information_schema.columns -> memory.main.information_schema_columns_compat
64+
// Views are created in the memory.main schema so they're always accessible regardless of default catalog
6565
// Skip transformation if catalog is explicitly "ducklake" - those are queries from
6666
// within the compat views that need to access the actual DuckLake information_schema
6767
if n.RangeVar != nil && strings.EqualFold(n.RangeVar.Schemaname, "information_schema") &&
6868
!strings.EqualFold(n.RangeVar.Catalogname, "ducklake") {
6969
relname := strings.ToLower(n.RangeVar.Relname)
7070
if newName, ok := t.ViewMappings[relname]; ok {
7171
n.RangeVar.Relname = newName
72-
// Views are in main schema (no catalog prefix needed)
73-
n.RangeVar.Catalogname = ""
72+
// Views are in memory.main schema - always use explicit catalog for DuckLake compatibility
73+
n.RangeVar.Catalogname = "memory"
7474
n.RangeVar.Schemaname = "main"
7575
*changed = true
7676
}

transpiler/transpiler_test.go

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -105,31 +105,31 @@ func TestTranspile_InformationSchema(t *testing.T) {
105105
{
106106
name: "information_schema.columns -> compat view",
107107
input: "SELECT * FROM information_schema.columns",
108-
contains: "information_schema_columns_compat",
108+
contains: "memory.main.information_schema_columns_compat",
109109
excludes: "information_schema.columns",
110110
},
111111
{
112112
name: "information_schema.tables -> compat view",
113113
input: "SELECT * FROM information_schema.tables",
114-
contains: "information_schema_tables_compat",
114+
contains: "memory.main.information_schema_tables_compat",
115115
excludes: "information_schema.tables",
116116
},
117117
{
118118
name: "information_schema.schemata -> compat view",
119119
input: "SELECT * FROM information_schema.schemata",
120-
contains: "information_schema_schemata_compat",
120+
contains: "memory.main.information_schema_schemata_compat",
121121
excludes: "information_schema.schemata",
122122
},
123123
{
124124
name: "INFORMATION_SCHEMA.COLUMNS uppercase -> compat view",
125125
input: "SELECT * FROM INFORMATION_SCHEMA.COLUMNS",
126-
contains: "information_schema_columns_compat",
126+
contains: "memory.main.information_schema_columns_compat",
127127
excludes: "information_schema.columns",
128128
},
129129
{
130130
name: "aliased information_schema query",
131131
input: "SELECT c.column_name FROM information_schema.columns c WHERE c.table_name = 'test'",
132-
contains: "information_schema_columns_compat",
132+
contains: "memory.main.information_schema_columns_compat",
133133
excludes: "information_schema.columns",
134134
},
135135
{
@@ -169,14 +169,14 @@ func TestTranspile_InformationSchema_DuckLakeMode(t *testing.T) {
169169
contains string
170170
}{
171171
{
172-
name: "information_schema.columns with DuckLake -> main qualified",
172+
name: "information_schema.columns with DuckLake -> memory.main qualified",
173173
input: "SELECT * FROM information_schema.columns",
174-
contains: "main.information_schema_columns_compat",
174+
contains: "memory.main.information_schema_columns_compat",
175175
},
176176
{
177-
name: "information_schema.tables with DuckLake -> main qualified",
177+
name: "information_schema.tables with DuckLake -> memory.main qualified",
178178
input: "SELECT * FROM information_schema.tables",
179-
contains: "main.information_schema_tables_compat",
179+
contains: "memory.main.information_schema_tables_compat",
180180
},
181181
}
182182

0 commit comments

Comments
 (0)