@@ -282,3 +282,176 @@ func initPgCatalog(db *sql.DB) error {
282282
283283 return nil
284284}
285+
286+ // initInformationSchema creates the column metadata table and information_schema wrapper views.
287+ // This enables accurate type information (VARCHAR lengths, NUMERIC precision) in information_schema.
288+ func initInformationSchema (db * sql.DB ) error {
289+ // Create metadata table to store column type information that DuckDB doesn't preserve
290+ metadataTableSQL := `
291+ CREATE TABLE IF NOT EXISTS __duckgres_column_metadata (
292+ table_schema VARCHAR NOT NULL,
293+ table_name VARCHAR NOT NULL,
294+ column_name VARCHAR NOT NULL,
295+ character_maximum_length INTEGER,
296+ numeric_precision INTEGER,
297+ numeric_scale INTEGER,
298+ PRIMARY KEY (table_schema, table_name, column_name)
299+ )
300+ `
301+ if _ , err := db .Exec (metadataTableSQL ); err != nil {
302+ // Table might already exist, that's OK
303+ // Ignore errors since PRIMARY KEY might not work in all contexts
304+ }
305+
306+ // Create information_schema.columns wrapper view
307+ // Transforms DuckDB type names to PostgreSQL-compatible names (e.g., VARCHAR -> text)
308+ // First try with metadata table join, fall back to simple view if table doesn't exist
309+ columnsViewWithMetaSQL := `
310+ CREATE OR REPLACE VIEW information_schema_columns_compat AS
311+ SELECT
312+ c.table_catalog,
313+ c.table_schema,
314+ c.table_name,
315+ c.column_name,
316+ c.ordinal_position,
317+ c.column_default,
318+ c.is_nullable,
319+ CASE
320+ WHEN UPPER(c.data_type) = 'VARCHAR' OR UPPER(c.data_type) LIKE 'VARCHAR(%' THEN 'text'
321+ ELSE c.data_type
322+ END AS data_type,
323+ COALESCE(m.character_maximum_length, c.character_maximum_length) AS character_maximum_length,
324+ c.character_octet_length,
325+ COALESCE(m.numeric_precision, c.numeric_precision) AS numeric_precision,
326+ COALESCE(m.numeric_scale, c.numeric_scale) AS numeric_scale,
327+ c.datetime_precision,
328+ NULL AS interval_type,
329+ NULL AS interval_precision,
330+ NULL AS character_set_catalog,
331+ NULL AS character_set_schema,
332+ NULL AS character_set_name,
333+ NULL AS collation_catalog,
334+ NULL AS collation_schema,
335+ NULL AS collation_name,
336+ NULL AS domain_catalog,
337+ NULL AS domain_schema,
338+ NULL AS domain_name,
339+ NULL AS udt_catalog,
340+ NULL AS udt_schema,
341+ NULL AS udt_name,
342+ NULL AS scope_catalog,
343+ NULL AS scope_schema,
344+ NULL AS scope_name,
345+ NULL AS maximum_cardinality,
346+ NULL AS dtd_identifier,
347+ 'NO' AS is_self_referencing,
348+ 'NO' AS is_identity,
349+ NULL AS identity_generation,
350+ NULL AS identity_start,
351+ NULL AS identity_increment,
352+ NULL AS identity_maximum,
353+ NULL AS identity_minimum,
354+ NULL AS identity_cycle,
355+ 'NEVER' AS is_generated,
356+ NULL AS generation_expression,
357+ 'YES' AS is_updatable
358+ FROM information_schema.columns c
359+ LEFT JOIN __duckgres_column_metadata m
360+ ON c.table_schema = m.table_schema
361+ AND c.table_name = m.table_name
362+ AND c.column_name = m.column_name
363+ `
364+ // Try with metadata table first
365+ if _ , err := db .Exec (columnsViewWithMetaSQL ); err != nil {
366+ // Metadata table doesn't exist, create simpler view without it
367+ columnsViewSimpleSQL := `
368+ CREATE OR REPLACE VIEW information_schema_columns_compat AS
369+ SELECT
370+ table_catalog,
371+ table_schema,
372+ table_name,
373+ column_name,
374+ ordinal_position,
375+ column_default,
376+ is_nullable,
377+ CASE
378+ WHEN UPPER(data_type) = 'VARCHAR' OR UPPER(data_type) LIKE 'VARCHAR(%' THEN 'text'
379+ ELSE data_type
380+ END AS data_type,
381+ character_maximum_length,
382+ character_octet_length,
383+ numeric_precision,
384+ numeric_scale,
385+ datetime_precision,
386+ NULL AS interval_type,
387+ NULL AS interval_precision,
388+ NULL AS character_set_catalog,
389+ NULL AS character_set_schema,
390+ NULL AS character_set_name,
391+ NULL AS collation_catalog,
392+ NULL AS collation_schema,
393+ NULL AS collation_name,
394+ NULL AS domain_catalog,
395+ NULL AS domain_schema,
396+ NULL AS domain_name,
397+ NULL AS udt_catalog,
398+ NULL AS udt_schema,
399+ NULL AS udt_name,
400+ NULL AS scope_catalog,
401+ NULL AS scope_schema,
402+ NULL AS scope_name,
403+ NULL AS maximum_cardinality,
404+ NULL AS dtd_identifier,
405+ 'NO' AS is_self_referencing,
406+ 'NO' AS is_identity,
407+ NULL AS identity_generation,
408+ NULL AS identity_start,
409+ NULL AS identity_increment,
410+ NULL AS identity_maximum,
411+ NULL AS identity_minimum,
412+ NULL AS identity_cycle,
413+ 'NEVER' AS is_generated,
414+ NULL AS generation_expression,
415+ 'YES' AS is_updatable
416+ FROM information_schema.columns
417+ `
418+ db .Exec (columnsViewSimpleSQL )
419+ }
420+
421+ // Create information_schema.tables wrapper view with additional PostgreSQL columns
422+ tablesViewSQL := `
423+ CREATE OR REPLACE VIEW information_schema_tables_compat AS
424+ SELECT
425+ t.table_catalog,
426+ t.table_schema,
427+ t.table_name,
428+ t.table_type,
429+ NULL AS self_referencing_column_name,
430+ NULL AS reference_generation,
431+ NULL AS user_defined_type_catalog,
432+ NULL AS user_defined_type_schema,
433+ NULL AS user_defined_type_name,
434+ 'YES' AS is_insertable_into,
435+ 'NO' AS is_typed,
436+ NULL AS commit_action
437+ FROM information_schema.tables t
438+ `
439+ db .Exec (tablesViewSQL )
440+
441+ // Create information_schema.schemata wrapper view
442+ schemataViewSQL := `
443+ CREATE OR REPLACE VIEW information_schema_schemata_compat AS
444+ SELECT
445+ s.catalog_name,
446+ s.schema_name,
447+ 'duckdb' AS schema_owner,
448+ NULL AS default_character_set_catalog,
449+ NULL AS default_character_set_schema,
450+ NULL AS default_character_set_name,
451+ NULL AS sql_path
452+ FROM information_schema.schemata s
453+ `
454+ db .Exec (schemataViewSQL )
455+
456+ return nil
457+ }
0 commit comments