diff --git a/jOOQ-codegen/src/main/java/org/jooq/codegen/JavaGenerator.java b/jOOQ-codegen/src/main/java/org/jooq/codegen/JavaGenerator.java index 8105d45612..aa29685224 100644 --- a/jOOQ-codegen/src/main/java/org/jooq/codegen/JavaGenerator.java +++ b/jOOQ-codegen/src/main/java/org/jooq/codegen/JavaGenerator.java @@ -1643,7 +1643,7 @@ public class JavaGenerator extends AbstractGenerator { for (TableDefinition table : database.getTables(schema)) { try { - if (table.isTableValuedFunction() && table.getReferencedTable() != table) + if (table.isTableValuedFunction() && table.getReferencedTableOrUDT() != table) continue; generateRecord(table); @@ -7204,7 +7204,7 @@ public class JavaGenerator extends AbstractGenerator { final String tableId = scala ? out.ref(getStrategy().getFullJavaIdentifier(table), 2) : getStrategy().getJavaIdentifier(table); - final String recordType = out.ref(getStrategy().getFullJavaClassName(table.getReferencedTable(), Mode.RECORD)); + final String recordType = out.ref(getStrategy().getFullJavaClassName(table.getReferencedTableOrUDT(), Mode.RECORD)); final String classExtends = out.ref(getStrategy().getJavaClassExtends(table, Mode.DEFAULT)); final List interfaces = out.ref(getStrategy().getJavaClassImplements(table, Mode.DEFAULT)); final String schemaId = generateDefaultSchema(schema) @@ -8578,7 +8578,7 @@ public class JavaGenerator extends AbstractGenerator { // [#10481] Use the types from replaced embeddables if applicable List replacingEmbeddablesAndUnreplacedColumns = replacingEmbeddablesAndUnreplacedColumns(table); int degree = replacingEmbeddablesAndUnreplacedColumns.size(); - int referencedDegree = replacingEmbeddablesAndUnreplacedColumns(table.getReferencedTable()).size(); + int referencedDegree = replacingEmbeddablesAndUnreplacedColumns(table.getReferencedTableOrUDT()).size(); String rowType = refRowType(out, replacingEmbeddablesAndUnreplacedColumns); String rowTypeContravariantJava = refRowType(out, replacingEmbeddablesAndUnreplacedColumns, s -> "? super " + s); @@ -11013,7 +11013,7 @@ public class JavaGenerator extends AbstractGenerator { return; } - final String recordClassName = out.ref(getStrategy().getFullJavaClassName(function.getReferencedTable(), Mode.RECORD)); + final String recordClassName = out.ref(getStrategy().getFullJavaClassName(function.getReferencedTableOrUDT(), Mode.RECORD)); // [#3456] Local variables should not collide with actual function arguments final String configurationArgument = disambiguateJavaMemberName(function.getParameters(), "configuration"); @@ -11067,7 +11067,11 @@ public class JavaGenerator extends AbstractGenerator { } protected void printRecordTypeMethod(JavaWriter out, Definition tableOrUDT) { - final String className = out.ref(getStrategy().getFullJavaClassName(tableOrUDT instanceof TableDefinition ? ((TableDefinition) tableOrUDT).getReferencedTable() : tableOrUDT, Mode.RECORD)); + final String className = out.ref(getStrategy().getFullJavaClassName( + tableOrUDT instanceof TableDefinition + ? ((TableDefinition) tableOrUDT).getReferencedTableOrUDT() + : tableOrUDT, Mode.RECORD + )); out.javadoc("The class holding records for this type"); diff --git a/jOOQ-meta/src/main/java/org/jooq/meta/AbstractTableDefinition.java b/jOOQ-meta/src/main/java/org/jooq/meta/AbstractTableDefinition.java index 45ae2e49ed..232ea384cd 100644 --- a/jOOQ-meta/src/main/java/org/jooq/meta/AbstractTableDefinition.java +++ b/jOOQ-meta/src/main/java/org/jooq/meta/AbstractTableDefinition.java @@ -71,6 +71,7 @@ implements private final SchemaDefinition referencedSchema; private final String referencedName; private TableDefinition referencedTable; + private Definition referencedTableOrUDT; public AbstractTableDefinition(SchemaDefinition schema, String name, String comment) { this(schema, name, comment, TableType.TABLE, null); @@ -353,6 +354,22 @@ implements return referencedTable; } + @Override + public final Definition getReferencedTableOrUDT() { + if (referencedTableOrUDT == null) { + if (referencedSchema != null) + referencedTableOrUDT = getDatabase().getTable(referencedSchema, referencedName); + + if (referencedTableOrUDT == null) + referencedTableOrUDT = getDatabase().getUDT(referencedSchema, referencedName); + + if (referencedTableOrUDT == null) + referencedTableOrUDT = this; + } + + return referencedTableOrUDT; + } + @Override protected List getElements0() throws SQLException { return null; diff --git a/jOOQ-meta/src/main/java/org/jooq/meta/TableDefinition.java b/jOOQ-meta/src/main/java/org/jooq/meta/TableDefinition.java index 7c22e60f61..0bdbe5a7c0 100644 --- a/jOOQ-meta/src/main/java/org/jooq/meta/TableDefinition.java +++ b/jOOQ-meta/src/main/java/org/jooq/meta/TableDefinition.java @@ -236,8 +236,31 @@ public interface TableDefinition extends Definition { * true and the table valued function references a table * type * + * + * @deprecated - [#7406] - 3.20.0 - Use {@link #getReferencedTableOrUDT()} + * instead. */ + @Deprecated @NotNull TableDefinition getReferencedTable(); + /** + * The referenced table or UDT type, if this + * {@link #isTableValuedFunction()}. + *

+ * This returns: + *

    + *
  • this, if {@link #isTableValuedFunction()} == + * false
  • + *
  • this, if {@link #isTableValuedFunction()} == + * true but the table valued function doesn't reference a table + * type
  • + *
  • Another {@link TableDefinition} or {@link UDTDefinition}, if + * {@link #isTableValuedFunction()} == true and the table + * valued function references a table type
  • + *
+ */ + @NotNull + Definition getReferencedTableOrUDT(); + } diff --git a/jOOQ-meta/src/main/java/org/jooq/meta/postgres/PostgresTableValuedFunction.java b/jOOQ-meta/src/main/java/org/jooq/meta/postgres/PostgresTableValuedFunction.java index 62941ecaee..208220c403 100644 --- a/jOOQ-meta/src/main/java/org/jooq/meta/postgres/PostgresTableValuedFunction.java +++ b/jOOQ-meta/src/main/java/org/jooq/meta/postgres/PostgresTableValuedFunction.java @@ -48,8 +48,10 @@ import static org.jooq.impl.DSL.row; import static org.jooq.impl.DSL.rowNumber; import static org.jooq.impl.DSL.select; import static org.jooq.impl.DSL.substring; +import static org.jooq.impl.DSL.val; import static org.jooq.impl.DSL.when; import static org.jooq.meta.postgres.PostgresRoutineDefinition.pNumericPrecision; +import static org.jooq.meta.postgres.information_schema.Tables.ATTRIBUTES; import static org.jooq.meta.postgres.information_schema.Tables.COLUMNS; import static org.jooq.meta.postgres.information_schema.Tables.PARAMETERS; import static org.jooq.meta.postgres.information_schema.Tables.ROUTINES; @@ -73,6 +75,7 @@ import org.jooq.meta.DefaultColumnDefinition; import org.jooq.meta.DefaultDataTypeDefinition; import org.jooq.meta.ParameterDefinition; import org.jooq.meta.SchemaDefinition; +import org.jooq.meta.postgres.information_schema.tables.Attributes; import org.jooq.meta.postgres.information_schema.tables.Columns; import org.jooq.meta.postgres.information_schema.tables.Parameters; import org.jooq.meta.postgres.information_schema.tables.Routines; @@ -112,6 +115,7 @@ public class PostgresTableValuedFunction extends AbstractTableDefinition { Routines r = ROUTINES.as("r"); Parameters p = PARAMETERS.as("p"); Columns c = COLUMNS.as("c"); + Attributes a = ATTRIBUTES.as("a"); Columns x = COLUMNS.as("x"); PgClass pg_c = PG_CLASS.as("pgc"); PgAttribute pg_a = PG_ATTRIBUTE.as("pga"); @@ -120,6 +124,7 @@ public class PostgresTableValuedFunction extends AbstractTableDefinition { Field pPrecision = pNumericPrecision(p); Field cPrecision = nvl(c.DATETIME_PRECISION, c.NUMERIC_PRECISION); + Field aPrecision = nvl(a.DATETIME_PRECISION, a.NUMERIC_PRECISION); Field rPrecision = nvl(r.DATETIME_PRECISION, r.NUMERIC_PRECISION); @@ -127,6 +132,7 @@ public class PostgresTableValuedFunction extends AbstractTableDefinition { + for (Record record : create() // [#3375] The first subselect is expected to return only those @@ -165,16 +171,17 @@ public class PostgresTableValuedFunction extends AbstractTableDefinition { // table-valued functions that return a SETOF [ table type ], as that // table reference is reported via a TYPE_UDT that matches a table // from INFORMATION_SCHEMA.TABLES + // [#7406] Exclude composite types select( - coalesce(c.COLUMN_NAME, getName()).as(c.COLUMN_NAME), - coalesce(c.ORDINAL_POSITION, inline(1)).as(c.ORDINAL_POSITION), + coalesce(c.COLUMN_NAME, a.ATTRIBUTE_NAME, val(getName())).as(c.COLUMN_NAME), + coalesce(c.ORDINAL_POSITION, a.ORDINAL_POSITION, inline(1)).as(c.ORDINAL_POSITION), db.arrayDataType(x.DATA_TYPE, x.UDT_NAME, pg_a.ATTNDIMS).as(c.DATA_TYPE), - coalesce(c.CHARACTER_MAXIMUM_LENGTH, r.CHARACTER_MAXIMUM_LENGTH ).as(c.CHARACTER_MAXIMUM_LENGTH), - coalesce(cPrecision, rPrecision).as(c.NUMERIC_PRECISION), - coalesce(c.NUMERIC_SCALE, r.NUMERIC_SCALE).as(c.NUMERIC_SCALE), - coalesce(c.IS_NULLABLE, inline("true")).as(c.IS_NULLABLE), - coalesce(c.COLUMN_DEFAULT, inline((String) null)).as(c.COLUMN_DEFAULT), - coalesce(c.UDT_SCHEMA, inline((String) null)).as(c.UDT_SCHEMA), + coalesce(c.CHARACTER_MAXIMUM_LENGTH, a.CHARACTER_MAXIMUM_LENGTH, r.CHARACTER_MAXIMUM_LENGTH ).as(c.CHARACTER_MAXIMUM_LENGTH), + coalesce(cPrecision, aPrecision, rPrecision).as(c.NUMERIC_PRECISION), + coalesce(c.NUMERIC_SCALE, a.NUMERIC_SCALE, r.NUMERIC_SCALE).as(c.NUMERIC_SCALE), + coalesce(c.IS_NULLABLE, a.IS_NULLABLE, inline("true")).as(c.IS_NULLABLE), + coalesce(c.COLUMN_DEFAULT, a.ATTRIBUTE_DEFAULT, inline((String) null)).as(c.COLUMN_DEFAULT), + coalesce(c.UDT_SCHEMA, a.ATTRIBUTE_UDT_SCHEMA, inline((String) null)).as(c.UDT_SCHEMA), db.arrayUdtName(x.DATA_TYPE, x.UDT_NAME).as(c.UDT_NAME) ) .from(r) @@ -185,20 +192,26 @@ public class PostgresTableValuedFunction extends AbstractTableDefinition { .join(pg_p) .on(pg_p.PRONAME.concat("_").concat(pg_p.OID).eq(r.SPECIFIC_NAME)) .and(pg_p.pgNamespace().NSPNAME.eq(r.SPECIFIC_SCHEMA)) + + // [#7406] COLUMNS and ATTRIBUTES are mutually exclusive, hence no cross product here .leftJoin(c) .on(row(r.TYPE_UDT_CATALOG, r.TYPE_UDT_SCHEMA, r.TYPE_UDT_NAME) .eq(c.TABLE_CATALOG, c.TABLE_SCHEMA, c.TABLE_NAME)) + .leftJoin(a) + .on(row(r.TYPE_UDT_CATALOG, r.TYPE_UDT_SCHEMA, r.TYPE_UDT_NAME) + .eq(a.UDT_CATALOG, a.UDT_SCHEMA, a.UDT_NAME)) + .leftJoin(pg_c) - .on(c.TABLE_NAME.eq(pg_c.RELNAME)) + .on(nvl(c.TABLE_NAME, a.UDT_NAME).eq(pg_c.RELNAME)) .and(pg_c.pgNamespace().NSPNAME.eq(r.SPECIFIC_SCHEMA)) .leftJoin(pg_a) .on(pg_c.OID.eq(pg_a.ATTRELID)) - .and(c.COLUMN_NAME.eq(pg_a.ATTNAME)) + .and(nvl(c.COLUMN_NAME, a.ATTRIBUTE_NAME).eq(pg_a.ATTNAME)) .crossApply( select( - coalesce(c.DATA_TYPE, r.DATA_TYPE).as(x.DATA_TYPE), + coalesce(c.DATA_TYPE, a.DATA_TYPE, r.DATA_TYPE).as(x.DATA_TYPE), coalesce( - c.UDT_NAME, r.UDT_NAME, + c.UDT_NAME, a.ATTRIBUTE_UDT_NAME, r.UDT_NAME, field(select(pg_t.TYPNAME).from(pg_t).where(pg_t.OID.eq(pg_p.PRORETTYPE))) ).as(x.UDT_NAME) ).asTable(x)