diff --git a/Dockerfile b/Dockerfile index 8756b7ac..df79412a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM node:20 as build +FROM node:20 AS build WORKDIR /usr/src/app # Do `npm ci` separately so we can cache `node_modules` # https://nodejs.org/en/docs/guides/nodejs-docker-webapp/ diff --git a/package.json b/package.json index 62315e9f..324e75a0 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,8 @@ "gen:types:go": "PG_META_GENERATE_TYPES=go node --loader ts-node/esm src/server/server.ts", "gen:types:swift": "PG_META_GENERATE_TYPES=swift node --loader ts-node/esm src/server/server.ts", "start": "node dist/server/server.js", - "dev": "trap 'npm run db:clean' INT && run-s db:clean db:run && nodemon --exec node --loader ts-node/esm src/server/server.ts | pino-pretty --colorize", + "dev": "trap 'npm run db:clean' INT && run-s db:clean db:run && run-s dev:code", + "dev:code": "nodemon --exec node --loader ts-node/esm src/server/server.ts | pino-pretty --colorize", "test": "run-s db:clean db:run test:run db:clean", "db:clean": "cd test/db && docker compose down", "db:run": "cd test/db && docker compose up --detach --wait", diff --git a/src/lib/PostgresMetaTypes.ts b/src/lib/PostgresMetaTypes.ts index 35371d55..a3d73fd6 100644 --- a/src/lib/PostgresMetaTypes.ts +++ b/src/lib/PostgresMetaTypes.ts @@ -33,7 +33,7 @@ export default class PostgresMetaTypes { t.typrelid = 0 or ( select - c.relkind ${includeTableTypes ? `in ('c', 'r')` : `= 'c'`} + c.relkind ${includeTableTypes ? `in ('c', 'r', 'v')` : `= 'c'`} from pg_class c where diff --git a/src/lib/sql/functions.sql b/src/lib/sql/functions.sql index d2258402..80ca4177 100644 --- a/src/lib/sql/functions.sql +++ b/src/lib/sql/functions.sql @@ -44,6 +44,25 @@ select pg_get_function_result(f.oid) as return_type, nullif(rt.typrelid::int8, 0) as return_type_relation_id, f.proretset as is_set_returning_function, + case + when f.proretset and rt.typrelid != 0 and exists ( + select 1 from pg_class c + where c.oid = rt.typrelid + -- exclude custom types relation from what is considered a set of table + and c.relkind in ('r', 'p', 'v', 'm', 'f') + ) then true + else false + end as returns_set_of_table, + case + when rt.typrelid != 0 then + (select relname from pg_class where oid = rt.typrelid) + else null + end as return_table_name, + case + when f.proretset then + coalesce(f.prorows, 0) > 1 + else false + end as returns_multiple_rows, case when f.provolatile = 'i' then 'IMMUTABLE' when f.provolatile = 's' then 'STABLE' @@ -76,32 +95,48 @@ from select oid, jsonb_agg(jsonb_build_object( - 'mode', t2.mode, + 'mode', mode, 'name', name, 'type_id', type_id, - 'has_default', has_default + 'has_default', has_default, + 'table_name', table_name )) as args from ( select - oid, - unnest(arg_modes) as mode, - unnest(arg_names) as name, - unnest(arg_types)::int8 as type_id, - unnest(arg_has_defaults) as has_default - from - functions - ) as t1, - lateral ( - select + t1.oid, + t2.mode, + t1.name, + t1.type_id, + t1.has_default, case - when t1.mode = 'i' then 'in' - when t1.mode = 'o' then 'out' - when t1.mode = 'b' then 'inout' - when t1.mode = 'v' then 'variadic' - else 'table' - end as mode - ) as t2 + when pt.typrelid != 0 then pc.relname + else null + end as table_name + from + ( + select + oid, + unnest(arg_modes) as mode, + unnest(arg_names) as name, + unnest(arg_types)::int8 as type_id, + unnest(arg_has_defaults) as has_default + from + functions + ) as t1 + cross join lateral ( + select + case + when t1.mode = 'i' then 'in' + when t1.mode = 'o' then 'out' + when t1.mode = 'b' then 'inout' + when t1.mode = 'v' then 'variadic' + else 'table' + end as mode + ) as t2 + left join pg_type pt on pt.oid = t1.type_id + left join pg_class pc on pc.oid = pt.typrelid + ) sub group by - t1.oid + oid ) f_args on f_args.oid = f.oid diff --git a/src/lib/types.ts b/src/lib/types.ts index bfd60250..4d4c2889 100644 --- a/src/lib/types.ts +++ b/src/lib/types.ts @@ -148,6 +148,7 @@ const postgresFunctionSchema = Type.Object({ name: Type.String(), type_id: Type.Number(), has_default: Type.Boolean(), + table_name: Type.Union([Type.String(), Type.Null()]), }) ), argument_types: Type.String(), @@ -156,6 +157,9 @@ const postgresFunctionSchema = Type.Object({ return_type: Type.String(), return_type_relation_id: Type.Union([Type.Integer(), Type.Null()]), is_set_returning_function: Type.Boolean(), + returns_set_of_table: Type.Boolean(), + return_table_name: Type.Union([Type.String(), Type.Null()]), + returns_multiple_rows: Type.Boolean(), behavior: Type.Union([ Type.Literal('IMMUTABLE'), Type.Literal('STABLE'), diff --git a/src/server/constants.ts b/src/server/constants.ts index 9354c59f..c64b45e6 100644 --- a/src/server/constants.ts +++ b/src/server/constants.ts @@ -51,6 +51,10 @@ export const GENERATE_TYPES_SWIFT_ACCESS_CONTROL = process.env ? (process.env.PG_META_GENERATE_TYPES_SWIFT_ACCESS_CONTROL as AccessControl) : 'internal' +// json/jsonb/text types +export const VALID_UNNAMED_FUNCTION_ARG_TYPES = new Set([114, 3802, 25]) +export const VALID_FUNCTION_ARGS_MODE = new Set(['in', 'inout', 'variadic']) + export const PG_META_MAX_RESULT_SIZE = process.env.PG_META_MAX_RESULT_SIZE_MB ? // Node-postgres get a maximum size in bytes make the conversion from the env variable // from MB to Bytes diff --git a/src/server/templates/typescript.ts b/src/server/templates/typescript.ts index 4f9cac03..c375f45f 100644 --- a/src/server/templates/typescript.ts +++ b/src/server/templates/typescript.ts @@ -8,7 +8,11 @@ import type { PostgresView, } from '../../lib/index.js' import type { GeneratorMetadata } from '../../lib/generators.js' -import { GENERATE_TYPES_DEFAULT_SCHEMA } from '../constants.js' +import { + GENERATE_TYPES_DEFAULT_SCHEMA, + VALID_FUNCTION_ARGS_MODE, + VALID_UNNAMED_FUNCTION_ARG_TYPES, +} from '../constants.js' export const apply = async ({ schemas, @@ -26,13 +30,346 @@ export const apply = async ({ detectOneToOneRelationships: boolean postgrestVersion?: string }): Promise => { + schemas.sort((a, b) => a.name.localeCompare(b.name)) + const columnsByTableId = Object.fromEntries( [...tables, ...foreignTables, ...views, ...materializedViews].map((t) => [t.id, []]) ) - columns - .filter((c) => c.table_id in columnsByTableId) - .sort(({ name: a }, { name: b }) => a.localeCompare(b)) - .forEach((c) => columnsByTableId[c.table_id].push(c)) + for (const column of columns) { + if (column.table_id in columnsByTableId) { + columnsByTableId[column.table_id].push(column) + } + } + for (const tableId in columnsByTableId) { + columnsByTableId[tableId].sort((a, b) => a.name.localeCompare(b.name)) + } + + const introspectionBySchema = Object.fromEntries<{ + tables: Pick[] + views: PostgresView[] + functions: { fn: PostgresFunction; inArgs: PostgresFunction['args'] }[] + enums: PostgresType[] + compositeTypes: PostgresType[] + }>( + schemas.map((s) => [ + s.name, + { tables: [], views: [], functions: [], enums: [], compositeTypes: [] }, + ]) + ) + for (const table of tables) { + if (table.schema in introspectionBySchema) { + introspectionBySchema[table.schema].tables.push(table) + } + } + for (const table of foreignTables) { + if (table.schema in introspectionBySchema) { + introspectionBySchema[table.schema].tables.push(table) + } + } + for (const view of views) { + if (view.schema in introspectionBySchema) { + introspectionBySchema[view.schema].views.push(view) + } + } + for (const materializedView of materializedViews) { + if (materializedView.schema in introspectionBySchema) { + introspectionBySchema[materializedView.schema].views.push({ + ...materializedView, + is_updatable: false, + }) + } + } + for (const func of functions) { + if (func.schema in introspectionBySchema) { + func.args.sort((a, b) => a.name.localeCompare(b.name)) + // Get all input args (in, inout, variadic modes) + const inArgs = func.args.filter(({ mode }) => VALID_FUNCTION_ARGS_MODE.has(mode)) + + if ( + // Case 1: Function has no parameters + inArgs.length === 0 || + // Case 2: All input args are named + !inArgs.some(({ name }) => name === '') || + // Case 3: All unnamed args have default values AND are valid types + inArgs.every((arg) => { + if (arg.name === '') { + return arg.has_default && VALID_UNNAMED_FUNCTION_ARG_TYPES.has(arg.type_id) + } + return true + }) || + // Case 4: Single unnamed parameter of valid type (json, jsonb, text) + // Exclude all functions definitions that have only one single argument unnamed argument that isn't + // a json/jsonb/text as it won't be considered by PostgREST + (inArgs.length === 1 && + inArgs[0].name === '' && + (VALID_UNNAMED_FUNCTION_ARG_TYPES.has(inArgs[0].type_id) || + // OR if the function have a single unnamed args which is another table (embeded function) + (inArgs[0].table_name && func.return_table_name) || + // OR if the function takes a table row but doesn't qualify as embedded (for error reporting) + (inArgs[0].table_name && !func.return_table_name))) + ) { + introspectionBySchema[func.schema].functions.push({ fn: func, inArgs }) + } + + // NOTE: Functions with named table arguments are generally excluded + // as they're not supported by PostgREST in the expected way + } + } + for (const type of types) { + if (type.schema in introspectionBySchema) { + if (type.enums.length > 0) { + introspectionBySchema[type.schema].enums.push(type) + } + if (type.attributes.length > 0) { + introspectionBySchema[type.schema].compositeTypes.push(type) + } + } + } + for (const schema in introspectionBySchema) { + introspectionBySchema[schema].tables.sort((a, b) => a.name.localeCompare(b.name)) + introspectionBySchema[schema].views.sort((a, b) => a.name.localeCompare(b.name)) + introspectionBySchema[schema].functions.sort((a, b) => a.fn.name.localeCompare(b.fn.name)) + introspectionBySchema[schema].enums.sort((a, b) => a.name.localeCompare(b.name)) + introspectionBySchema[schema].compositeTypes.sort((a, b) => a.name.localeCompare(b.name)) + } + + // group types by id for quicker lookup + const typesById = types.reduce( + (acc, type) => { + acc[type.id] = type + return acc + }, + {} as Record + ) + + const getFunctionTsReturnType = (fn: PostgresFunction, returnType: string) => { + // Determine if this function should have SetofOptions + let setofOptionsInfo = '' + + // Only add SetofOptions for functions with table arguments (embedded functions) + // or specific functions that RETURNS table-name + if (fn.args.length === 1 && fn.args[0].table_name) { + // Case 1: Standard embedded function with proper setof detection + if (fn.returns_set_of_table && fn.return_table_name) { + setofOptionsInfo = `SetofOptions: { + from: ${JSON.stringify(typesById[fn.args[0].type_id].format)} + to: ${JSON.stringify(fn.return_table_name)} + isOneToOne: ${fn.returns_multiple_rows ? false : true} + isSetofReturn: true + }` + } + // Case 2: Handle RETURNS table-name those are always a one to one relationship + else if (fn.return_table_name && !fn.returns_set_of_table) { + const sourceTable = typesById[fn.args[0].type_id].format + let targetTable = fn.return_table_name + setofOptionsInfo = `SetofOptions: { + from: ${JSON.stringify(sourceTable)} + to: ${JSON.stringify(targetTable)} + isOneToOne: true + isSetofReturn: false + }` + } + } + // Case 3: Special case for functions without table arguments still returning a table + // Those can be used in rpc to select sub fields of a table + else if (fn.return_table_name) { + setofOptionsInfo = `SetofOptions: { + from: "*" + to: ${JSON.stringify(fn.return_table_name)} + isOneToOne: ${fn.returns_multiple_rows ? false : true} + isSetofReturn: ${fn.is_set_returning_function} + }` + } + + return `${returnType}${fn.is_set_returning_function && fn.returns_multiple_rows ? '[]' : ''} + ${setofOptionsInfo ? `${setofOptionsInfo}` : ''}` + } + + const getFunctionReturnType = (schema: PostgresSchema, fn: PostgresFunction): string => { + // Case 1: `returns table`. + const tableArgs = fn.args.filter(({ mode }) => mode === 'table') + if (tableArgs.length > 0) { + const argsNameAndType = tableArgs.map(({ name, type_id }) => { + const type = typesById[type_id] + let tsType = 'unknown' + if (type) { + tsType = pgTypeToTsType(schema, type.name, { types, schemas, tables, views }) + } + return { name, type: tsType } + }) + + return `{ + ${argsNameAndType.map(({ name, type }) => `${JSON.stringify(name)}: ${type}`)} + }` + } + + // Case 2: returns a relation's row type. + const relation = + introspectionBySchema[schema.name]?.tables.find( + ({ id }) => id === fn.return_type_relation_id + ) || + introspectionBySchema[schema.name]?.views.find(({ id }) => id === fn.return_type_relation_id) + if (relation) { + return `{ + ${columnsByTableId[relation.id] + .map( + (column) => + `${JSON.stringify(column.name)}: ${pgTypeToTsType(schema, column.format, { + types, + schemas, + tables, + views, + })} ${column.is_nullable ? '| null' : ''}` + ) + .join(',\n')} + }` + } + + // Case 3: returns base/array/composite/enum type. + const type = typesById[fn.return_type_id] + if (type) { + return pgTypeToTsType(schema, type.name, { types, schemas, tables, views }) + } + + return 'unknown' + } + // Special error case for functions that take table row but don't qualify as embedded functions + const hasTableRowError = (fn: PostgresFunction, inArgs: PostgresFunction['args']) => { + if ( + inArgs.length === 1 && + inArgs[0].name === '' && + inArgs[0].table_name && + !fn.return_table_name + ) { + return true + } + return false + } + + // Check for generic conflict cases that need error reporting + const getConflictError = ( + schema: PostgresSchema, + fns: Array<{ fn: PostgresFunction; inArgs: PostgresFunction['args'] }>, + fn: PostgresFunction, + inArgs: PostgresFunction['args'] + ) => { + // If there is a single function definition, there is no conflict + if (fns.length <= 1) return null + + // Generic conflict detection patterns + // Pattern 1: No-args vs default-args conflicts + if (inArgs.length === 0) { + const conflictingFns = fns.filter(({ fn: otherFn, inArgs: otherInArgs }) => { + if (otherFn === fn) return false + return otherInArgs.length === 1 && otherInArgs[0].name === '' && otherInArgs[0].has_default + }) + + if (conflictingFns.length > 0) { + const conflictingFn = conflictingFns[0] + const returnTypeName = typesById[conflictingFn.fn.return_type_id]?.name || 'unknown' + return `Could not choose the best candidate function between: ${schema.name}.${fn.name}(), ${schema.name}.${fn.name}( => ${returnTypeName}). Try renaming the parameters or the function itself in the database so function overloading can be resolved` + } + } + + // Pattern 2: Same parameter name but different types (unresolvable overloads) + if (inArgs.length === 1 && inArgs[0].name !== '') { + const conflictingFns = fns.filter(({ fn: otherFn, inArgs: otherInArgs }) => { + if (otherFn === fn) return false + return ( + otherInArgs.length === 1 && + otherInArgs[0].name === inArgs[0].name && + otherInArgs[0].type_id !== inArgs[0].type_id + ) + }) + + if (conflictingFns.length > 0) { + const allConflictingFunctions = [{ fn, inArgs }, ...conflictingFns] + const conflictList = allConflictingFunctions + .sort((a, b) => { + const aArgs = a.inArgs + const bArgs = b.inArgs + return (aArgs[0]?.type_id || 0) - (bArgs[0]?.type_id || 0) + }) + .map((f) => { + const args = f.inArgs + return `${schema.name}.${fn.name}(${args.map((a) => `${a.name || ''} => ${typesById[a.type_id]?.name || 'unknown'}`).join(', ')})` + }) + .join(', ') + + return `Could not choose the best candidate function between: ${conflictList}. Try renaming the parameters or the function itself in the database so function overloading can be resolved` + } + } + + return null + } + + const getFunctionSignatures = ( + schema: PostgresSchema, + fns: Array<{ fn: PostgresFunction; inArgs: PostgresFunction['args'] }> + ) => { + return fns.map(({ fn, inArgs }) => { + let argsType = 'never' + let returnType = getFunctionReturnType(schema, fn) + + // Check for specific error cases + const conflictError = getConflictError(schema, fns, fn, inArgs) + if (conflictError) { + if (inArgs.length > 0) { + const argsNameAndType = inArgs.map(({ name, type_id, has_default }) => { + const type = typesById[type_id] + let tsType = 'unknown' + if (type) { + tsType = pgTypeToTsType(schema, type.name, { + types, + schemas, + tables, + views, + }) + } + return { name, type: tsType, has_default } + }) + argsType = `{ ${argsNameAndType.map(({ name, type, has_default }) => `${JSON.stringify(name)}${has_default ? '?' : ''}: ${type}`)} }` + } + returnType = `{ error: true } & ${JSON.stringify(conflictError)}` + } else if (hasTableRowError(fn, inArgs)) { + // Special case for computed fields returning scalars functions + if (inArgs.length > 0) { + const argsNameAndType = inArgs.map(({ name, type_id, has_default }) => { + const type = typesById[type_id] + let tsType = 'unknown' + if (type) { + tsType = pgTypeToTsType(schema, type.name, { + types, + schemas, + tables, + views, + }) + } + return { name, type: tsType, has_default } + }) + argsType = `{ ${argsNameAndType.map(({ name, type, has_default }) => `${JSON.stringify(name)}${has_default ? '?' : ''}: ${type}`)} }` + } + returnType = `{ error: true } & ${JSON.stringify(`the function ${schema.name}.${fn.name} with parameter or with a single unnamed json/jsonb parameter, but no matches were found in the schema cache`)}` + } else if (inArgs.length > 0) { + const argsNameAndType = inArgs.map(({ name, type_id, has_default }) => { + const type = typesById[type_id] + let tsType = 'unknown' + if (type) { + tsType = pgTypeToTsType(schema, type.name, { + types, + schemas, + tables, + views, + }) + } + return { name, type: tsType, has_default } + }) + argsType = `{ ${argsNameAndType.map(({ name, type, has_default }) => `${JSON.stringify(name)}${has_default ? '?' : ''}: ${type}`)} }` + } + + return `{ Args: ${argsType}; Returns: ${getFunctionTsReturnType(fn, returnType)} }` + }) + } const internal_supabase_schema = postgrestVersion ? `// Allows to automatically instantiate createClient with right options @@ -47,44 +384,15 @@ export type Json = string | number | boolean | null | { [key: string]: Json | un export type Database = { ${internal_supabase_schema} - ${schemas - .sort(({ name: a }, { name: b }) => a.localeCompare(b)) - .map((schema) => { - const schemaTables = [...tables, ...foreignTables] - .filter((table) => table.schema === schema.name) - .sort(({ name: a }, { name: b }) => a.localeCompare(b)) - const schemaViews = [...views, ...materializedViews] - .filter((view) => view.schema === schema.name) - .sort(({ name: a }, { name: b }) => a.localeCompare(b)) - const schemaFunctions = functions - .filter((func) => { - if (func.schema !== schema.name) { - return false - } - - // Either: - // 1. All input args are be named, or - // 2. There is only one input arg which is unnamed - const inArgs = func.args.filter(({ mode }) => ['in', 'inout', 'variadic'].includes(mode)) - - if (!inArgs.some(({ name }) => name === '')) { - return true - } - - if (inArgs.length === 1) { - return true - } - - return false - }) - .sort(({ name: a }, { name: b }) => a.localeCompare(b)) - const schemaEnums = types - .filter((type) => type.schema === schema.name && type.enums.length > 0) - .sort(({ name: a }, { name: b }) => a.localeCompare(b)) - const schemaCompositeTypes = types - .filter((type) => type.schema === schema.name && type.attributes.length > 0) - .sort(({ name: a }, { name: b }) => a.localeCompare(b)) - return `${JSON.stringify(schema.name)}: { + ${schemas.map((schema) => { + const { + tables: schemaTables, + views: schemaViews, + functions: schemaFunctions, + enums: schemaEnums, + compositeTypes: schemaCompositeTypes, + } = introspectionBySchema[schema.name] + return `${JSON.stringify(schema.name)}: { Tables: { ${ schemaTables.length === 0 @@ -103,20 +411,11 @@ export type Database = { })} ${column.is_nullable ? '| null' : ''}` ), ...schemaFunctions - .filter((fn) => fn.argument_types === table.name) - .map((fn) => { - const type = types.find(({ id }) => id === fn.return_type_id) - let tsType = 'unknown' - if (type) { - tsType = pgTypeToTsType(schema, type.name, { - types, - schemas, - tables, - views, - }) - } - return `${JSON.stringify(fn.name)}: ${tsType} | null` - }), + .filter(({ fn }) => fn.argument_types === table.name) + .map( + ({ fn }) => + `${JSON.stringify(fn.name)}: ${getFunctionReturnType(schema, fn)} | null` + ), ]} } Insert: { @@ -213,18 +512,26 @@ export type Database = { : schemaViews.map( (view) => `${JSON.stringify(view.name)}: { Row: { - ${columnsByTableId[view.id].map( - (column) => - `${JSON.stringify(column.name)}: ${pgTypeToTsType(schema, column.format, { - types, - schemas, - tables, - views, - })} ${column.is_nullable ? '| null' : ''}` - )} + ${[ + ...columnsByTableId[view.id].map( + (column) => + `${JSON.stringify(column.name)}: ${pgTypeToTsType(schema, column.format, { + types, + schemas, + tables, + views, + })} ${column.is_nullable ? '| null' : ''}` + ), + ...schemaFunctions + .filter(({ fn }) => fn.argument_types === view.name) + .map( + ({ fn }) => + `${JSON.stringify(fn.name)}: ${getFunctionReturnType(schema, fn)} | null` + ), + ]} } ${ - 'is_updatable' in view && view.is_updatable + view.is_updatable ? `Insert: { ${columnsByTableId[view.id].map((column) => { let output = JSON.stringify(column.name) @@ -301,111 +608,24 @@ export type Database = { if (schemaFunctions.length === 0) { return '[_ in never]: never' } - const schemaFunctionsGroupedByName = schemaFunctions.reduce( (acc, curr) => { - acc[curr.name] ??= [] - acc[curr.name].push(curr) + acc[curr.fn.name] ??= [] + acc[curr.fn.name].push(curr) return acc }, - {} as Record + {} as Record ) + for (const fnName in schemaFunctionsGroupedByName) { + schemaFunctionsGroupedByName[fnName].sort((a, b) => + b.fn.definition.localeCompare(a.fn.definition) + ) + } - return Object.entries(schemaFunctionsGroupedByName).map( - ([fnName, fns]) => - `${JSON.stringify(fnName)}: { - Args: ${fns - .map(({ args }) => { - const inArgs = args - .toSorted((a, b) => a.name.localeCompare(b.name)) - .filter(({ mode }) => mode === 'in') - - if (inArgs.length === 0) { - return 'Record' - } - - const argsNameAndType = inArgs.map(({ name, type_id, has_default }) => { - const type = types.find(({ id }) => id === type_id) - let tsType = 'unknown' - if (type) { - tsType = pgTypeToTsType(schema, type.name, { - types, - schemas, - tables, - views, - }) - } - return { name, type: tsType, has_default } - }) - return `{ ${argsNameAndType.map(({ name, type, has_default }) => `${JSON.stringify(name)}${has_default ? '?' : ''}: ${type}`)} }` - }) - .toSorted() - // A function can have multiples definitions with differents args, but will always return the same type - .join(' | ')} - Returns: ${(() => { - // Case 1: `returns table`. - const tableArgs = fns[0].args.filter(({ mode }) => mode === 'table') - if (tableArgs.length > 0) { - const argsNameAndType = tableArgs.map(({ name, type_id }) => { - const type = types.find(({ id }) => id === type_id) - let tsType = 'unknown' - if (type) { - tsType = pgTypeToTsType(schema, type.name, { - types, - schemas, - tables, - views, - }) - } - return { name, type: tsType } - }) - - return `{ - ${argsNameAndType - .toSorted((a, b) => a.name.localeCompare(b.name)) - .map(({ name, type }) => `${JSON.stringify(name)}: ${type}`)} - }` - } - - // Case 2: returns a relation's row type. - const relation = [...tables, ...views].find( - ({ id }) => id === fns[0].return_type_relation_id - ) - if (relation) { - return `{ - ${columnsByTableId[relation.id] - .toSorted((a, b) => a.name.localeCompare(b.name)) - .map( - (column) => - `${JSON.stringify(column.name)}: ${pgTypeToTsType( - schema, - column.format, - { - types, - schemas, - tables, - views, - } - )} ${column.is_nullable ? '| null' : ''}` - )} - }` - } - - // Case 3: returns base/array/composite/enum type. - const type = types.find(({ id }) => id === fns[0].return_type_id) - if (type) { - return pgTypeToTsType(schema, type.name, { - types, - schemas, - tables, - views, - }) - } - - return 'unknown' - })()}${fns[0].is_set_returning_function ? '[]' : ''} - }` - ) + return Object.entries(schemaFunctionsGroupedByName).map(([fnName, fns]) => { + const functionSignatures = getFunctionSignatures(schema, fns) + return `${JSON.stringify(fnName)}:\n${functionSignatures.map((sig) => `| ${sig}`).join('\n')}` + }) })()} } Enums: { @@ -428,7 +648,7 @@ export type Database = { ({ name, attributes }) => `${JSON.stringify(name)}: { ${attributes.map(({ name, type_id }) => { - const type = types.find(({ id }) => id === type_id) + const type = typesById[type_id] let tsType = 'unknown' if (type) { tsType = `${pgTypeToTsType(schema, type.name, { @@ -445,7 +665,7 @@ export type Database = { } } }` - })} + })} } type DatabaseWithoutInternals = Omit @@ -554,13 +774,9 @@ export type CompositeTypes< : never export const Constants = { - ${schemas - .sort(({ name: a }, { name: b }) => a.localeCompare(b)) - .map((schema) => { - const schemaEnums = types - .filter((type) => type.schema === schema.name && type.enums.length > 0) - .sort(({ name: a }, { name: b }) => a.localeCompare(b)) - return `${JSON.stringify(schema.name)}: { + ${schemas.map((schema) => { + const schemaEnums = introspectionBySchema[schema.name]?.enums ?? [] + return `${JSON.stringify(schema.name)}: { Enums: { ${schemaEnums.map( (enum_) => @@ -570,7 +786,7 @@ export const Constants = { )} } }` - })} + })} } as const ` diff --git a/test/db/00-init.sql b/test/db/00-init.sql index 3551a4e7..ae5b8200 100644 --- a/test/db/00-init.sql +++ b/test/db/00-init.sql @@ -56,6 +56,17 @@ $$ language plpgsql; CREATE VIEW todos_view AS SELECT * FROM public.todos; -- For testing typegen on view-to-view relationships create view users_view as select * from public.users; +-- Create a more complex view for testing +CREATE VIEW user_todos_summary_view AS +SELECT + u.id as user_id, + u.name as user_name, + u.status as user_status, + COUNT(t.id) as todo_count, + array_agg(t.details) FILTER (WHERE t.details IS NOT NULL) as todo_details +FROM public.users u +LEFT JOIN public.todos t ON t."user-id" = u.id +GROUP BY u.id, u.name, u.status; create materialized view todos_matview as select * from public.todos; @@ -69,6 +80,11 @@ $$ select substring($1.details, 1, 3); $$ language sql stable; +create function public.blurb_varchar(public.todos_view) returns character varying as +$$ +select substring($1.details, 1, 3); +$$ language sql stable; + create function public.details_length(public.todos) returns integer as $$ select length($1.details); @@ -101,6 +117,15 @@ as $$ select * from public.users limit 1; $$; +create or replace function public.function_returning_single_row(todos public.todos) +returns public.users +language sql +stable +as $$ + select * from public.users limit 1; +$$; + + create or replace function public.function_returning_set_of_rows() returns setof public.users language sql @@ -117,6 +142,15 @@ as $$ select id, name from public.users; $$; +create or replace function public.function_returning_table_with_args(user_id int) +returns table (id int, name text) +language sql +stable +as $$ + select id, name from public.users WHERE id = user_id; +$$; + + create or replace function public.polymorphic_function(text) returns void language sql as ''; create or replace function public.polymorphic_function(bool) returns void language sql as ''; @@ -182,3 +216,202 @@ LANGUAGE SQL STABLE AS $$ SELECT * FROM public.todos WHERE "user-id" = todo_row."user-id"; $$; + +-- SETOF composite_type - Returns multiple rows of a custom composite type +CREATE OR REPLACE FUNCTION public.get_composite_type_data() +RETURNS SETOF composite_type_with_array_attribute +LANGUAGE SQL STABLE +AS $$ + SELECT ROW(ARRAY['hello', 'world']::text[])::composite_type_with_array_attribute + UNION ALL + SELECT ROW(ARRAY['foo', 'bar']::text[])::composite_type_with_array_attribute; +$$; + +-- SETOF record - Returns multiple rows with structure defined in the function +CREATE OR REPLACE FUNCTION public.get_user_summary() +RETURNS SETOF record +LANGUAGE SQL STABLE +AS $$ + SELECT u.id, name, count(t.id) as todo_count + FROM public.users u + LEFT JOIN public.todos t ON t."user-id" = u.id + GROUP BY u.id, u.name; +$$; + +-- SETOF scalar_type - Returns multiple values of a basic type +CREATE OR REPLACE FUNCTION public.get_user_ids() +RETURNS SETOF bigint +LANGUAGE SQL STABLE +AS $$ + SELECT id FROM public.users; +$$; + + +-- Function returning view using scalar as input +CREATE OR REPLACE FUNCTION public.get_single_user_summary_from_view(search_user_id bigint) +RETURNS SETOF user_todos_summary_view +LANGUAGE SQL STABLE +ROWS 1 +AS $$ + SELECT * FROM user_todos_summary_view WHERE user_id = search_user_id; +$$; +-- Function returning view using table row as input +CREATE OR REPLACE FUNCTION public.get_single_user_summary_from_view(user_row users) +RETURNS SETOF user_todos_summary_view +LANGUAGE SQL STABLE +ROWS 1 +AS $$ + SELECT * FROM user_todos_summary_view WHERE user_id = user_row.id; +$$; +-- Function returning view using another view row as input +CREATE OR REPLACE FUNCTION public.get_single_user_summary_from_view(userview_row users_view) +RETURNS SETOF user_todos_summary_view +LANGUAGE SQL STABLE +ROWS 1 +AS $$ + SELECT * FROM user_todos_summary_view WHERE user_id = userview_row.id; +$$; + + +-- Function returning view using scalar as input +CREATE OR REPLACE FUNCTION public.get_todos_from_user(search_user_id bigint) +RETURNS SETOF todos +LANGUAGE SQL STABLE +AS $$ + SELECT * FROM todos WHERE "user-id" = search_user_id; +$$; +-- Function returning view using table row as input +CREATE OR REPLACE FUNCTION public.get_todos_from_user(user_row users) +RETURNS SETOF todos +LANGUAGE SQL STABLE +AS $$ + SELECT * FROM todos WHERE "user-id" = user_row.id; +$$; +-- Function returning view using another view row as input +CREATE OR REPLACE FUNCTION public.get_todos_from_user(userview_row users_view) +RETURNS SETOF todos +LANGUAGE SQL STABLE +AS $$ + SELECT * FROM todos WHERE "user-id" = userview_row.id; +$$; + +-- Valid postgresql function override but that produce an unresolvable postgrest function call +create function postgrest_unresolvable_function() returns void language sql as ''; +create function postgrest_unresolvable_function(a text) returns int language sql as 'select 1'; +create function postgrest_unresolvable_function(a int) returns text language sql as $$ + SELECT 'toto' +$$; +-- Valid postgresql function override with differents returns types depending of different arguments +create function postgrest_resolvable_with_override_function() returns void language sql as ''; +create function postgrest_resolvable_with_override_function(a text) returns int language sql as 'select 1'; +create function postgrest_resolvable_with_override_function(b int) returns text language sql as $$ + SELECT 'toto' +$$; +-- Function overrides returning setof tables +create function postgrest_resolvable_with_override_function(user_id bigint) returns setof users language sql stable as $$ + SELECT * FROM users WHERE id = user_id; +$$; +create function postgrest_resolvable_with_override_function(todo_id bigint, completed boolean) returns setof todos language sql stable as $$ + SELECT * FROM todos WHERE id = todo_id AND completed = completed; +$$; +-- Function override taking a table as argument and returning a setof +create function postgrest_resolvable_with_override_function(user_row users) returns setof todos language sql stable as $$ + SELECT * FROM todos WHERE "user-id" = user_row.id; +$$; + +create or replace function public.polymorphic_function_with_different_return(bool) returns int language sql as 'SELECT 1'; +create or replace function public.polymorphic_function_with_different_return(int) returns int language sql as 'SELECT 2'; +create or replace function public.polymorphic_function_with_different_return(text) returns text language sql as $$ SELECT 'foo' $$; + +create or replace function public.polymorphic_function_with_no_params_or_unnamed() returns int language sql as 'SELECT 1'; +create or replace function public.polymorphic_function_with_no_params_or_unnamed(bool) returns int language sql as 'SELECT 2'; +create or replace function public.polymorphic_function_with_no_params_or_unnamed(text) returns text language sql as $$ SELECT 'foo' $$; +-- Function with a single unnamed params that isn't a json/jsonb/text should never appears in the type gen as it won't be in postgrest schema +create or replace function public.polymorphic_function_with_unnamed_integer(int) returns int language sql as 'SELECT 1'; +create or replace function public.polymorphic_function_with_unnamed_json(json) returns int language sql as 'SELECT 1'; +create or replace function public.polymorphic_function_with_unnamed_jsonb(jsonb) returns int language sql as 'SELECT 1'; +create or replace function public.polymorphic_function_with_unnamed_text(text) returns int language sql as 'SELECT 1'; + +-- Functions with unnamed parameters that have default values +create or replace function public.polymorphic_function_with_unnamed_default() returns int language sql as 'SELECT 1'; +create or replace function public.polymorphic_function_with_unnamed_default(int default 42) returns int language sql as 'SELECT 2'; +create or replace function public.polymorphic_function_with_unnamed_default(text default 'default') returns text language sql as $$ SELECT 'foo' $$; + +-- Functions with unnamed parameters that have default values and multiple overloads +create or replace function public.polymorphic_function_with_unnamed_default_overload() returns int language sql as 'SELECT 1'; +create or replace function public.polymorphic_function_with_unnamed_default_overload(int default 42) returns int language sql as 'SELECT 2'; +create or replace function public.polymorphic_function_with_unnamed_default_overload(text default 'default') returns text language sql as $$ SELECT 'foo' $$; +create or replace function public.polymorphic_function_with_unnamed_default_overload(bool default true) returns int language sql as 'SELECT 3'; + +-- Test function with unnamed row parameter returning setof +CREATE OR REPLACE FUNCTION public.test_unnamed_row_setof(todos) +RETURNS SETOF todos +LANGUAGE SQL STABLE +AS $$ + SELECT * FROM public.todos WHERE "user-id" = $1."user-id"; +$$; + +CREATE OR REPLACE FUNCTION public.test_unnamed_row_setof(users) +RETURNS SETOF todos +LANGUAGE SQL STABLE +AS $$ + SELECT * FROM public.todos WHERE "user-id" = $1."id"; +$$; + + +CREATE OR REPLACE FUNCTION public.test_unnamed_row_setof(user_id bigint) +RETURNS SETOF todos +LANGUAGE SQL STABLE +AS $$ + SELECT * FROM public.todos WHERE "user-id" = user_id; +$$; + +-- Test function with unnamed row parameter returning scalar +CREATE OR REPLACE FUNCTION public.test_unnamed_row_scalar(todos) +RETURNS integer +LANGUAGE SQL STABLE +AS $$ + SELECT COUNT(*) FROM public.todos WHERE "user-id" = $1."user-id"; +$$; + +-- Test function with unnamed view row parameter +CREATE OR REPLACE FUNCTION public.test_unnamed_view_row(todos_view) +RETURNS SETOF todos +LANGUAGE SQL STABLE +AS $$ + SELECT * FROM public.todos WHERE "user-id" = $1."user-id"; +$$; + +-- Test function with multiple unnamed row parameters +CREATE OR REPLACE FUNCTION public.test_unnamed_multiple_rows(users, todos) +RETURNS SETOF todos +LANGUAGE SQL STABLE +AS $$ + SELECT * FROM public.todos + WHERE "user-id" = $1.id + AND id = $2.id; +$$; + +-- Test function with unnamed row parameter returning composite +CREATE OR REPLACE FUNCTION public.test_unnamed_row_composite(users) +RETURNS composite_type_with_array_attribute +LANGUAGE SQL STABLE +AS $$ + SELECT ROW(ARRAY[$1.name])::composite_type_with_array_attribute; +$$; + +-- Function that returns a single element +CREATE OR REPLACE FUNCTION public.function_using_table_returns(user_row users) +RETURNS todos +LANGUAGE SQL STABLE +AS $$ + SELECT * FROM public.todos WHERE todos."user-id" = user_row.id LIMIT 1; +$$; + +CREATE OR REPLACE FUNCTION public.function_using_setof_rows_one(user_row users) +RETURNS SETOF todos +LANGUAGE SQL STABLE +ROWS 1 +AS $$ + SELECT * FROM public.todos WHERE todos."user-id" = user_row.id LIMIT 1; +$$; diff --git a/test/lib/functions.ts b/test/lib/functions.ts index 05de3244..084f2b8f 100644 --- a/test/lib/functions.ts +++ b/test/lib/functions.ts @@ -12,12 +12,14 @@ test('list', async () => { "has_default": false, "mode": "in", "name": "", + "table_name": null, "type_id": 23, }, { "has_default": false, "mode": "in", "name": "", + "table_name": null, "type_id": 23, }, ], @@ -36,9 +38,12 @@ test('list', async () => { "is_set_returning_function": false, "language": "sql", "name": "add", + "return_table_name": null, "return_type": "integer", "return_type_id": 23, "return_type_relation_id": null, + "returns_multiple_rows": false, + "returns_set_of_table": false, "schema": "public", "security_definer": false, } @@ -46,6 +51,137 @@ test('list', async () => { ) }) +test('list set-returning function with single object limit', async () => { + const res = await pgMeta.functions.list() + expect(res.data?.filter(({ name }) => name === 'get_user_audit_setof_single_row')) + .toMatchInlineSnapshot(` + [ + { + "args": [ + { + "has_default": false, + "mode": "in", + "name": "user_row", + "table_name": "users", + "type_id": 16395, + }, + ], + "argument_types": "user_row users", + "behavior": "STABLE", + "complete_statement": "CREATE OR REPLACE FUNCTION public.get_user_audit_setof_single_row(user_row users) + RETURNS SETOF users_audit + LANGUAGE sql + STABLE ROWS 1 + AS $function$ + SELECT * FROM public.users_audit WHERE user_id = user_row.id; + $function$ + ", + "config_params": null, + "definition": " + SELECT * FROM public.users_audit WHERE user_id = user_row.id; + ", + "id": 16506, + "identity_argument_types": "user_row users", + "is_set_returning_function": true, + "language": "sql", + "name": "get_user_audit_setof_single_row", + "return_table_name": "users_audit", + "return_type": "SETOF users_audit", + "return_type_id": 16418, + "return_type_relation_id": 16416, + "returns_multiple_rows": false, + "returns_set_of_table": true, + "schema": "public", + "security_definer": false, + }, + ] + `) +}) + +test('list set-returning function with multiples definitions', async () => { + const res = await pgMeta.functions.list() + expect(res.data?.filter(({ name }) => name === 'get_todos_setof_rows')).toMatchInlineSnapshot(` + [ + { + "args": [ + { + "has_default": false, + "mode": "in", + "name": "user_row", + "table_name": "users", + "type_id": 16395, + }, + ], + "argument_types": "user_row users", + "behavior": "STABLE", + "complete_statement": "CREATE OR REPLACE FUNCTION public.get_todos_setof_rows(user_row users) + RETURNS SETOF todos + LANGUAGE sql + STABLE + AS $function$ + SELECT * FROM public.todos WHERE "user-id" = user_row.id; + $function$ + ", + "config_params": null, + "definition": " + SELECT * FROM public.todos WHERE "user-id" = user_row.id; + ", + "id": 16507, + "identity_argument_types": "user_row users", + "is_set_returning_function": true, + "language": "sql", + "name": "get_todos_setof_rows", + "return_table_name": "todos", + "return_type": "SETOF todos", + "return_type_id": 16404, + "return_type_relation_id": 16402, + "returns_multiple_rows": true, + "returns_set_of_table": true, + "schema": "public", + "security_definer": false, + }, + { + "args": [ + { + "has_default": false, + "mode": "in", + "name": "todo_row", + "table_name": "todos", + "type_id": 16404, + }, + ], + "argument_types": "todo_row todos", + "behavior": "STABLE", + "complete_statement": "CREATE OR REPLACE FUNCTION public.get_todos_setof_rows(todo_row todos) + RETURNS SETOF todos + LANGUAGE sql + STABLE + AS $function$ + SELECT * FROM public.todos WHERE "user-id" = todo_row."user-id"; + $function$ + ", + "config_params": null, + "definition": " + SELECT * FROM public.todos WHERE "user-id" = todo_row."user-id"; + ", + "id": 16508, + "identity_argument_types": "todo_row todos", + "is_set_returning_function": true, + "language": "sql", + "name": "get_todos_setof_rows", + "return_table_name": "todos", + "return_type": "SETOF todos", + "return_type_id": 16404, + "return_type_relation_id": 16402, + "returns_multiple_rows": true, + "returns_set_of_table": true, + "schema": "public", + "security_definer": false, + }, + ] + `) +}) + test('list functions with included schemas', async () => { let res = await pgMeta.functions.list({ includedSchemas: ['public'], @@ -107,12 +243,14 @@ test('retrieve, create, update, delete', async () => { "has_default": false, "mode": "in", "name": "a", + "table_name": null, "type_id": 21, }, { "has_default": false, "mode": "in", "name": "b", + "table_name": null, "type_id": 21, }, ], @@ -136,9 +274,12 @@ test('retrieve, create, update, delete', async () => { "is_set_returning_function": false, "language": "sql", "name": "test_func", + "return_table_name": null, "return_type": "integer", "return_type_id": 23, "return_type_relation_id": null, + "returns_multiple_rows": false, + "returns_set_of_table": false, "schema": "public", "security_definer": true, }, @@ -157,12 +298,14 @@ test('retrieve, create, update, delete', async () => { "has_default": false, "mode": "in", "name": "a", + "table_name": null, "type_id": 21, }, { "has_default": false, "mode": "in", "name": "b", + "table_name": null, "type_id": 21, }, ], @@ -186,9 +329,12 @@ test('retrieve, create, update, delete', async () => { "is_set_returning_function": false, "language": "sql", "name": "test_func", + "return_table_name": null, "return_type": "integer", "return_type_id": 23, "return_type_relation_id": null, + "returns_multiple_rows": false, + "returns_set_of_table": false, "schema": "public", "security_definer": true, }, @@ -211,12 +357,14 @@ test('retrieve, create, update, delete', async () => { "has_default": false, "mode": "in", "name": "a", + "table_name": null, "type_id": 21, }, { "has_default": false, "mode": "in", "name": "b", + "table_name": null, "type_id": 21, }, ], @@ -240,9 +388,12 @@ test('retrieve, create, update, delete', async () => { "is_set_returning_function": false, "language": "sql", "name": "test_func_renamed", + "return_table_name": null, "return_type": "integer", "return_type_id": 23, "return_type_relation_id": null, + "returns_multiple_rows": false, + "returns_set_of_table": false, "schema": "test_schema", "security_definer": true, }, @@ -261,12 +412,14 @@ test('retrieve, create, update, delete', async () => { "has_default": false, "mode": "in", "name": "a", + "table_name": null, "type_id": 21, }, { "has_default": false, "mode": "in", "name": "b", + "table_name": null, "type_id": 21, }, ], @@ -290,9 +443,12 @@ test('retrieve, create, update, delete', async () => { "is_set_returning_function": false, "language": "sql", "name": "test_func_renamed", + "return_table_name": null, "return_type": "integer", "return_type_id": 23, "return_type_relation_id": null, + "returns_multiple_rows": false, + "returns_set_of_table": false, "schema": "test_schema", "security_definer": true, }, @@ -345,9 +501,12 @@ test('retrieve set-returning function', async () => { "is_set_returning_function": true, "language": "sql", "name": "function_returning_set_of_rows", + "return_table_name": "users", "return_type": "SETOF users", "return_type_id": Any, "return_type_relation_id": Any, + "returns_multiple_rows": true, + "returns_set_of_table": true, "schema": "public", "security_definer": false, } diff --git a/test/server/typegen.ts b/test/server/typegen.ts index 76ac6218..03f93b45 100644 --- a/test/server/typegen.ts +++ b/test/server/typegen.ts @@ -129,6 +129,12 @@ test('typegen: typescript', async () => { details_is_long: boolean | null details_length: number | null details_words: string[] | null + test_unnamed_row_scalar: number | null + test_unnamed_row_setof: { + details: string | null + id: number + "user-id": number + } | null } Insert: { details?: string | null @@ -150,24 +156,1089 @@ test('typegen: typescript', async () => { { foreignKeyName: "todos_user-id_fkey" columns: ["user-id"] + referencedRelation: "user_todos_summary_view" + referencedColumns: ["user_id"] + }, + { + foreignKeyName: "todos_user-id_fkey" + columns: ["user-id"] + referencedRelation: "users" + referencedColumns: ["id"] + }, + { + foreignKeyName: "todos_user-id_fkey" + columns: ["user-id"] + referencedRelation: "users_view" + referencedColumns: ["id"] + }, + { + foreignKeyName: "todos_user-id_fkey" + columns: ["user-id"] + referencedRelation: "users_view_with_multiple_refs_to_users" + referencedColumns: ["initial_id"] + }, + { + foreignKeyName: "todos_user-id_fkey" + columns: ["user-id"] + referencedRelation: "users_view_with_multiple_refs_to_users" + referencedColumns: ["second_id"] + }, + ] + } + user_details: { + Row: { + details: string | null + user_id: number + } + Insert: { + details?: string | null + user_id: number + } + Update: { + details?: string | null + user_id?: number + } + Relationships: [ + { + foreignKeyName: "user_details_user_id_fkey" + columns: ["user_id"] + referencedRelation: "a_view" + referencedColumns: ["id"] + }, + { + foreignKeyName: "user_details_user_id_fkey" + columns: ["user_id"] + referencedRelation: "user_todos_summary_view" + referencedColumns: ["user_id"] + }, + { + foreignKeyName: "user_details_user_id_fkey" + columns: ["user_id"] + referencedRelation: "users" + referencedColumns: ["id"] + }, + { + foreignKeyName: "user_details_user_id_fkey" + columns: ["user_id"] + referencedRelation: "users_view" + referencedColumns: ["id"] + }, + { + foreignKeyName: "user_details_user_id_fkey" + columns: ["user_id"] + referencedRelation: "users_view_with_multiple_refs_to_users" + referencedColumns: ["initial_id"] + }, + { + foreignKeyName: "user_details_user_id_fkey" + columns: ["user_id"] + referencedRelation: "users_view_with_multiple_refs_to_users" + referencedColumns: ["second_id"] + }, + ] + } + users: { + Row: { + decimal: number | null + id: number + name: string | null + status: Database["public"]["Enums"]["user_status"] | null + test_unnamed_row_composite: + | Database["public"]["CompositeTypes"]["composite_type_with_array_attribute"] + | null + test_unnamed_row_setof: { + details: string | null + id: number + "user-id": number + } | null + } + Insert: { + decimal?: number | null + id?: number + name?: string | null + status?: Database["public"]["Enums"]["user_status"] | null + } + Update: { + decimal?: number | null + id?: number + name?: string | null + status?: Database["public"]["Enums"]["user_status"] | null + } + Relationships: [] + } + users_audit: { + Row: { + created_at: string | null + id: number + previous_value: Json | null + user_id: number | null + } + Insert: { + created_at?: string | null + id?: number + previous_value?: Json | null + user_id?: number | null + } + Update: { + created_at?: string | null + id?: number + previous_value?: Json | null + user_id?: number | null + } + Relationships: [] + } + } + Views: { + a_view: { + Row: { + id: number | null + } + Insert: { + id?: number | null + } + Update: { + id?: number | null + } + Relationships: [] + } + todos_matview: { + Row: { + details: string | null + id: number | null + "user-id": number | null + } + Relationships: [ + { + foreignKeyName: "todos_user-id_fkey" + columns: ["user-id"] + referencedRelation: "a_view" + referencedColumns: ["id"] + }, + { + foreignKeyName: "todos_user-id_fkey" + columns: ["user-id"] + referencedRelation: "user_todos_summary_view" + referencedColumns: ["user_id"] + }, + { + foreignKeyName: "todos_user-id_fkey" + columns: ["user-id"] + referencedRelation: "users" + referencedColumns: ["id"] + }, + { + foreignKeyName: "todos_user-id_fkey" + columns: ["user-id"] + referencedRelation: "users_view" + referencedColumns: ["id"] + }, + { + foreignKeyName: "todos_user-id_fkey" + columns: ["user-id"] + referencedRelation: "users_view_with_multiple_refs_to_users" + referencedColumns: ["initial_id"] + }, + { + foreignKeyName: "todos_user-id_fkey" + columns: ["user-id"] + referencedRelation: "users_view_with_multiple_refs_to_users" + referencedColumns: ["second_id"] + }, + ] + } + todos_view: { + Row: { + details: string | null + id: number | null + "user-id": number | null + blurb_varchar: string | null + test_unnamed_view_row: { + details: string | null + id: number + "user-id": number + } | null + } + Insert: { + details?: string | null + id?: number | null + "user-id"?: number | null + } + Update: { + details?: string | null + id?: number | null + "user-id"?: number | null + } + Relationships: [ + { + foreignKeyName: "todos_user-id_fkey" + columns: ["user-id"] + referencedRelation: "a_view" + referencedColumns: ["id"] + }, + { + foreignKeyName: "todos_user-id_fkey" + columns: ["user-id"] + referencedRelation: "user_todos_summary_view" + referencedColumns: ["user_id"] + }, + { + foreignKeyName: "todos_user-id_fkey" + columns: ["user-id"] + referencedRelation: "users" + referencedColumns: ["id"] + }, + { + foreignKeyName: "todos_user-id_fkey" + columns: ["user-id"] + referencedRelation: "users_view" + referencedColumns: ["id"] + }, + { + foreignKeyName: "todos_user-id_fkey" + columns: ["user-id"] + referencedRelation: "users_view_with_multiple_refs_to_users" + referencedColumns: ["initial_id"] + }, + { + foreignKeyName: "todos_user-id_fkey" + columns: ["user-id"] + referencedRelation: "users_view_with_multiple_refs_to_users" + referencedColumns: ["second_id"] + }, + ] + } + user_todos_summary_view: { + Row: { + todo_count: number | null + todo_details: string[] | null + user_id: number | null + user_name: string | null + user_status: Database["public"]["Enums"]["user_status"] | null + } + Relationships: [] + } + users_view: { + Row: { + decimal: number | null + id: number | null + name: string | null + status: Database["public"]["Enums"]["user_status"] | null + } + Insert: { + decimal?: number | null + id?: number | null + name?: string | null + status?: Database["public"]["Enums"]["user_status"] | null + } + Update: { + decimal?: number | null + id?: number | null + name?: string | null + status?: Database["public"]["Enums"]["user_status"] | null + } + Relationships: [] + } + users_view_with_multiple_refs_to_users: { + Row: { + initial_id: number | null + initial_name: string | null + second_id: number | null + second_name: string | null + } + Relationships: [] + } + } + Functions: { + blurb: { + Args: { "": Database["public"]["Tables"]["todos"]["Row"] } + Returns: { + error: true + } & "the function public.blurb with parameter or with a single unnamed json/jsonb parameter, but no matches were found in the schema cache" + } + blurb_varchar: + | { + Args: { "": Database["public"]["Tables"]["todos"]["Row"] } + Returns: { + error: true + } & "the function public.blurb_varchar with parameter or with a single unnamed json/jsonb parameter, but no matches were found in the schema cache" + } + | { + Args: { "": Database["public"]["Views"]["todos_view"]["Row"] } + Returns: { + error: true + } & "the function public.blurb_varchar with parameter or with a single unnamed json/jsonb parameter, but no matches were found in the schema cache" + } + details_is_long: { + Args: { "": Database["public"]["Tables"]["todos"]["Row"] } + Returns: { + error: true + } & "the function public.details_is_long with parameter or with a single unnamed json/jsonb parameter, but no matches were found in the schema cache" + } + details_length: { + Args: { "": Database["public"]["Tables"]["todos"]["Row"] } + Returns: { + error: true + } & "the function public.details_length with parameter or with a single unnamed json/jsonb parameter, but no matches were found in the schema cache" + } + details_words: { + Args: { "": Database["public"]["Tables"]["todos"]["Row"] } + Returns: { + error: true + } & "the function public.details_words with parameter or with a single unnamed json/jsonb parameter, but no matches were found in the schema cache" + } + function_returning_row: { + Args: never + Returns: { + decimal: number | null + id: number + name: string | null + status: Database["public"]["Enums"]["user_status"] | null + } + SetofOptions: { + from: "*" + to: "users" + isOneToOne: true + isSetofReturn: false + } + } + function_returning_set_of_rows: { + Args: never + Returns: { + decimal: number | null + id: number + name: string | null + status: Database["public"]["Enums"]["user_status"] | null + }[] + SetofOptions: { + from: "*" + to: "users" + isOneToOne: false + isSetofReturn: true + } + } + function_returning_single_row: { + Args: { todos: Database["public"]["Tables"]["todos"]["Row"] } + Returns: { + decimal: number | null + id: number + name: string | null + status: Database["public"]["Enums"]["user_status"] | null + } + SetofOptions: { + from: "todos" + to: "users" + isOneToOne: true + isSetofReturn: false + } + } + function_returning_table: { + Args: never + Returns: { + id: number + name: string + }[] + } + function_returning_table_with_args: { + Args: { user_id: number } + Returns: { + id: number + name: string + }[] + } + function_using_setof_rows_one: { + Args: { user_row: Database["public"]["Tables"]["users"]["Row"] } + Returns: { + details: string | null + id: number + "user-id": number + } + SetofOptions: { + from: "users" + to: "todos" + isOneToOne: true + isSetofReturn: true + } + } + function_using_table_returns: { + Args: { user_row: Database["public"]["Tables"]["users"]["Row"] } + Returns: { + details: string | null + id: number + "user-id": number + } + SetofOptions: { + from: "users" + to: "todos" + isOneToOne: true + isSetofReturn: false + } + } + get_composite_type_data: { + Args: never + Returns: Database["public"]["CompositeTypes"]["composite_type_with_array_attribute"][] + SetofOptions: { + from: "*" + to: "composite_type_with_array_attribute" + isOneToOne: false + isSetofReturn: true + } + } + get_single_user_summary_from_view: + | { + Args: { + userview_row: Database["public"]["Views"]["users_view"]["Row"] + } + Returns: { + todo_count: number | null + todo_details: string[] | null + user_id: number | null + user_name: string | null + user_status: Database["public"]["Enums"]["user_status"] | null + } + SetofOptions: { + from: "users_view" + to: "user_todos_summary_view" + isOneToOne: true + isSetofReturn: true + } + } + | { + Args: { user_row: Database["public"]["Tables"]["users"]["Row"] } + Returns: { + todo_count: number | null + todo_details: string[] | null + user_id: number | null + user_name: string | null + user_status: Database["public"]["Enums"]["user_status"] | null + } + SetofOptions: { + from: "users" + to: "user_todos_summary_view" + isOneToOne: true + isSetofReturn: true + } + } + | { + Args: { search_user_id: number } + Returns: { + todo_count: number | null + todo_details: string[] | null + user_id: number | null + user_name: string | null + user_status: Database["public"]["Enums"]["user_status"] | null + } + SetofOptions: { + from: "*" + to: "user_todos_summary_view" + isOneToOne: true + isSetofReturn: true + } + } + get_todos_from_user: + | { + Args: { + userview_row: Database["public"]["Views"]["users_view"]["Row"] + } + Returns: { + details: string | null + id: number + "user-id": number + }[] + SetofOptions: { + from: "users_view" + to: "todos" + isOneToOne: false + isSetofReturn: true + } + } + | { + Args: { user_row: Database["public"]["Tables"]["users"]["Row"] } + Returns: { + details: string | null + id: number + "user-id": number + }[] + SetofOptions: { + from: "users" + to: "todos" + isOneToOne: false + isSetofReturn: true + } + } + | { + Args: { search_user_id: number } + Returns: { + details: string | null + id: number + "user-id": number + }[] + SetofOptions: { + from: "*" + to: "todos" + isOneToOne: false + isSetofReturn: true + } + } + get_todos_setof_rows: + | { + Args: { user_row: Database["public"]["Tables"]["users"]["Row"] } + Returns: { + details: string | null + id: number + "user-id": number + }[] + SetofOptions: { + from: "users" + to: "todos" + isOneToOne: false + isSetofReturn: true + } + } + | { + Args: { todo_row: Database["public"]["Tables"]["todos"]["Row"] } + Returns: { + details: string | null + id: number + "user-id": number + }[] + SetofOptions: { + from: "todos" + to: "todos" + isOneToOne: false + isSetofReturn: true + } + } + get_user_audit_setof_single_row: { + Args: { user_row: Database["public"]["Tables"]["users"]["Row"] } + Returns: { + created_at: string | null + id: number + previous_value: Json | null + user_id: number | null + } + SetofOptions: { + from: "users" + to: "users_audit" + isOneToOne: true + isSetofReturn: true + } + } + get_user_ids: { Args: never; Returns: number[] } + get_user_summary: { Args: never; Returns: Record[] } + polymorphic_function: { Args: { "": string }; Returns: undefined } + polymorphic_function_with_different_return: { + Args: { "": string } + Returns: string + } + polymorphic_function_with_no_params_or_unnamed: + | { Args: never; Returns: number } + | { Args: { "": string }; Returns: string } + polymorphic_function_with_unnamed_default: + | { + Args: never + Returns: { + error: true + } & "Could not choose the best candidate function between: public.polymorphic_function_with_unnamed_default(), public.polymorphic_function_with_unnamed_default( => text). Try renaming the parameters or the function itself in the database so function overloading can be resolved" + } + | { Args: { ""?: string }; Returns: string } + polymorphic_function_with_unnamed_default_overload: + | { + Args: never + Returns: { + error: true + } & "Could not choose the best candidate function between: public.polymorphic_function_with_unnamed_default_overload(), public.polymorphic_function_with_unnamed_default_overload( => text). Try renaming the parameters or the function itself in the database so function overloading can be resolved" + } + | { Args: { ""?: string }; Returns: string } + polymorphic_function_with_unnamed_json: { + Args: { "": Json } + Returns: number + } + polymorphic_function_with_unnamed_jsonb: { + Args: { "": Json } + Returns: number + } + polymorphic_function_with_unnamed_text: { + Args: { "": string } + Returns: number + } + postgres_fdw_disconnect: { Args: { "": string }; Returns: boolean } + postgres_fdw_disconnect_all: { Args: never; Returns: boolean } + postgres_fdw_get_connections: { + Args: never + Returns: Record[] + } + postgres_fdw_handler: { Args: never; Returns: unknown } + postgrest_resolvable_with_override_function: + | { Args: { a: string }; Returns: number } + | { + Args: { user_id: number } + Returns: { + decimal: number | null + id: number + name: string | null + status: Database["public"]["Enums"]["user_status"] | null + }[] + SetofOptions: { + from: "*" + to: "users" + isOneToOne: false + isSetofReturn: true + } + } + | { + Args: { completed: boolean; todo_id: number } + Returns: { + details: string | null + id: number + "user-id": number + }[] + SetofOptions: { + from: "*" + to: "todos" + isOneToOne: false + isSetofReturn: true + } + } + | { + Args: { user_row: Database["public"]["Tables"]["users"]["Row"] } + Returns: { + details: string | null + id: number + "user-id": number + }[] + SetofOptions: { + from: "users" + to: "todos" + isOneToOne: false + isSetofReturn: true + } + } + | { Args: { b: number }; Returns: string } + | { Args: never; Returns: undefined } + postgrest_unresolvable_function: + | { + Args: { a: string } + Returns: { + error: true + } & "Could not choose the best candidate function between: public.postgrest_unresolvable_function(a => int4), public.postgrest_unresolvable_function(a => text). Try renaming the parameters or the function itself in the database so function overloading can be resolved" + } + | { + Args: { a: number } + Returns: { + error: true + } & "Could not choose the best candidate function between: public.postgrest_unresolvable_function(a => int4), public.postgrest_unresolvable_function(a => text). Try renaming the parameters or the function itself in the database so function overloading can be resolved" + } + | { Args: never; Returns: undefined } + test_internal_query: { Args: never; Returns: undefined } + test_unnamed_row_composite: { + Args: { "": Database["public"]["Tables"]["users"]["Row"] } + Returns: Database["public"]["CompositeTypes"]["composite_type_with_array_attribute"] + SetofOptions: { + from: "users" + to: "composite_type_with_array_attribute" + isOneToOne: true + isSetofReturn: false + } + } + test_unnamed_row_scalar: { + Args: { "": Database["public"]["Tables"]["todos"]["Row"] } + Returns: { + error: true + } & "the function public.test_unnamed_row_scalar with parameter or with a single unnamed json/jsonb parameter, but no matches were found in the schema cache" + } + test_unnamed_row_setof: + | { + Args: { user_id: number } + Returns: { + details: string | null + id: number + "user-id": number + }[] + SetofOptions: { + from: "*" + to: "todos" + isOneToOne: false + isSetofReturn: true + } + } + | { + Args: { "": Database["public"]["Tables"]["todos"]["Row"] } + Returns: { + details: string | null + id: number + "user-id": number + }[] + SetofOptions: { + from: "todos" + to: "todos" + isOneToOne: false + isSetofReturn: true + } + } + | { + Args: { "": Database["public"]["Tables"]["users"]["Row"] } + Returns: { + details: string | null + id: number + "user-id": number + }[] + SetofOptions: { + from: "users" + to: "todos" + isOneToOne: false + isSetofReturn: true + } + } + test_unnamed_view_row: { + Args: { "": Database["public"]["Views"]["todos_view"]["Row"] } + Returns: { + details: string | null + id: number + "user-id": number + }[] + SetofOptions: { + from: "todos_view" + to: "todos" + isOneToOne: false + isSetofReturn: true + } + } + } + Enums: { + meme_status: "new" | "old" | "retired" + user_status: "ACTIVE" | "INACTIVE" + } + CompositeTypes: { + composite_type_with_array_attribute: { + my_text_array: string[] | null + } + composite_type_with_record_attribute: { + todo: Database["public"]["Tables"]["todos"]["Row"] | null + } + } + } + } + + type DatabaseWithoutInternals = Omit + + type DefaultSchema = DatabaseWithoutInternals[Extract] + + export type Tables< + DefaultSchemaTableNameOrOptions extends + | keyof (DefaultSchema["Tables"] & DefaultSchema["Views"]) + | { schema: keyof DatabaseWithoutInternals }, + TableName extends DefaultSchemaTableNameOrOptions extends { + schema: keyof DatabaseWithoutInternals + } + ? keyof (DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Tables"] & + DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Views"]) + : never = never, + > = DefaultSchemaTableNameOrOptions extends { + schema: keyof DatabaseWithoutInternals + } + ? (DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Tables"] & + DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Views"])[TableName] extends { + Row: infer R + } + ? R + : never + : DefaultSchemaTableNameOrOptions extends keyof (DefaultSchema["Tables"] & + DefaultSchema["Views"]) + ? (DefaultSchema["Tables"] & + DefaultSchema["Views"])[DefaultSchemaTableNameOrOptions] extends { + Row: infer R + } + ? R + : never + : never + + export type TablesInsert< + DefaultSchemaTableNameOrOptions extends + | keyof DefaultSchema["Tables"] + | { schema: keyof DatabaseWithoutInternals }, + TableName extends DefaultSchemaTableNameOrOptions extends { + schema: keyof DatabaseWithoutInternals + } + ? keyof DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Tables"] + : never = never, + > = DefaultSchemaTableNameOrOptions extends { + schema: keyof DatabaseWithoutInternals + } + ? DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Tables"][TableName] extends { + Insert: infer I + } + ? I + : never + : DefaultSchemaTableNameOrOptions extends keyof DefaultSchema["Tables"] + ? DefaultSchema["Tables"][DefaultSchemaTableNameOrOptions] extends { + Insert: infer I + } + ? I + : never + : never + + export type TablesUpdate< + DefaultSchemaTableNameOrOptions extends + | keyof DefaultSchema["Tables"] + | { schema: keyof DatabaseWithoutInternals }, + TableName extends DefaultSchemaTableNameOrOptions extends { + schema: keyof DatabaseWithoutInternals + } + ? keyof DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Tables"] + : never = never, + > = DefaultSchemaTableNameOrOptions extends { + schema: keyof DatabaseWithoutInternals + } + ? DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Tables"][TableName] extends { + Update: infer U + } + ? U + : never + : DefaultSchemaTableNameOrOptions extends keyof DefaultSchema["Tables"] + ? DefaultSchema["Tables"][DefaultSchemaTableNameOrOptions] extends { + Update: infer U + } + ? U + : never + : never + + export type Enums< + DefaultSchemaEnumNameOrOptions extends + | keyof DefaultSchema["Enums"] + | { schema: keyof DatabaseWithoutInternals }, + EnumName extends DefaultSchemaEnumNameOrOptions extends { + schema: keyof DatabaseWithoutInternals + } + ? keyof DatabaseWithoutInternals[DefaultSchemaEnumNameOrOptions["schema"]]["Enums"] + : never = never, + > = DefaultSchemaEnumNameOrOptions extends { + schema: keyof DatabaseWithoutInternals + } + ? DatabaseWithoutInternals[DefaultSchemaEnumNameOrOptions["schema"]]["Enums"][EnumName] + : DefaultSchemaEnumNameOrOptions extends keyof DefaultSchema["Enums"] + ? DefaultSchema["Enums"][DefaultSchemaEnumNameOrOptions] + : never + + export type CompositeTypes< + PublicCompositeTypeNameOrOptions extends + | keyof DefaultSchema["CompositeTypes"] + | { schema: keyof DatabaseWithoutInternals }, + CompositeTypeName extends PublicCompositeTypeNameOrOptions extends { + schema: keyof DatabaseWithoutInternals + } + ? keyof DatabaseWithoutInternals[PublicCompositeTypeNameOrOptions["schema"]]["CompositeTypes"] + : never = never, + > = PublicCompositeTypeNameOrOptions extends { + schema: keyof DatabaseWithoutInternals + } + ? DatabaseWithoutInternals[PublicCompositeTypeNameOrOptions["schema"]]["CompositeTypes"][CompositeTypeName] + : PublicCompositeTypeNameOrOptions extends keyof DefaultSchema["CompositeTypes"] + ? DefaultSchema["CompositeTypes"][PublicCompositeTypeNameOrOptions] + : never + + export const Constants = { + public: { + Enums: { + meme_status: ["new", "old", "retired"], + user_status: ["ACTIVE", "INACTIVE"], + }, + }, + } as const + " + ` + ) +}) + +test('typegen w/ one-to-one relationships', async () => { + const { body } = await app.inject({ + method: 'GET', + path: '/generators/typescript', + query: { detect_one_to_one_relationships: 'true' }, + }) + expect(body).toMatchInlineSnapshot( + ` + "export type Json = + | string + | number + | boolean + | null + | { [key: string]: Json | undefined } + | Json[] + + export type Database = { + public: { + Tables: { + category: { + Row: { + id: number + name: string + } + Insert: { + id?: number + name: string + } + Update: { + id?: number + name?: string + } + Relationships: [] + } + empty: { + Row: {} + Insert: {} + Update: {} + Relationships: [] + } + foreign_table: { + Row: { + id: number + name: string | null + status: Database["public"]["Enums"]["user_status"] | null + } + Insert: { + id: number + name?: string | null + status?: Database["public"]["Enums"]["user_status"] | null + } + Update: { + id?: number + name?: string | null + status?: Database["public"]["Enums"]["user_status"] | null + } + Relationships: [] + } + memes: { + Row: { + category: number | null + created_at: string + id: number + metadata: Json | null + name: string + status: Database["public"]["Enums"]["meme_status"] | null + } + Insert: { + category?: number | null + created_at: string + id?: number + metadata?: Json | null + name: string + status?: Database["public"]["Enums"]["meme_status"] | null + } + Update: { + category?: number | null + created_at?: string + id?: number + metadata?: Json | null + name?: string + status?: Database["public"]["Enums"]["meme_status"] | null + } + Relationships: [ + { + foreignKeyName: "memes_category_fkey" + columns: ["category"] + isOneToOne: false + referencedRelation: "category" + referencedColumns: ["id"] + }, + ] + } + table_with_other_tables_row_type: { + Row: { + col1: Database["public"]["Tables"]["user_details"]["Row"] | null + col2: Database["public"]["Views"]["a_view"]["Row"] | null + } + Insert: { + col1?: Database["public"]["Tables"]["user_details"]["Row"] | null + col2?: Database["public"]["Views"]["a_view"]["Row"] | null + } + Update: { + col1?: Database["public"]["Tables"]["user_details"]["Row"] | null + col2?: Database["public"]["Views"]["a_view"]["Row"] | null + } + Relationships: [] + } + table_with_primary_key_other_than_id: { + Row: { + name: string | null + other_id: number + } + Insert: { + name?: string | null + other_id?: number + } + Update: { + name?: string | null + other_id?: number + } + Relationships: [] + } + todos: { + Row: { + details: string | null + id: number + "user-id": number + blurb: string | null + blurb_varchar: string | null + details_is_long: boolean | null + details_length: number | null + details_words: string[] | null + test_unnamed_row_scalar: number | null + test_unnamed_row_setof: { + details: string | null + id: number + "user-id": number + } | null + } + Insert: { + details?: string | null + id?: number + "user-id": number + } + Update: { + details?: string | null + id?: number + "user-id"?: number + } + Relationships: [ + { + foreignKeyName: "todos_user-id_fkey" + columns: ["user-id"] + isOneToOne: false + referencedRelation: "a_view" + referencedColumns: ["id"] + }, + { + foreignKeyName: "todos_user-id_fkey" + columns: ["user-id"] + isOneToOne: false + referencedRelation: "user_todos_summary_view" + referencedColumns: ["user_id"] + }, + { + foreignKeyName: "todos_user-id_fkey" + columns: ["user-id"] + isOneToOne: false referencedRelation: "users" referencedColumns: ["id"] }, { foreignKeyName: "todos_user-id_fkey" columns: ["user-id"] + isOneToOne: false referencedRelation: "users_view" referencedColumns: ["id"] }, { foreignKeyName: "todos_user-id_fkey" columns: ["user-id"] + isOneToOne: false referencedRelation: "users_view_with_multiple_refs_to_users" referencedColumns: ["initial_id"] }, { foreignKeyName: "todos_user-id_fkey" columns: ["user-id"] + isOneToOne: false referencedRelation: "users_view_with_multiple_refs_to_users" referencedColumns: ["second_id"] }, @@ -190,30 +1261,42 @@ test('typegen: typescript', async () => { { foreignKeyName: "user_details_user_id_fkey" columns: ["user_id"] + isOneToOne: true referencedRelation: "a_view" referencedColumns: ["id"] }, { foreignKeyName: "user_details_user_id_fkey" columns: ["user_id"] + isOneToOne: true + referencedRelation: "user_todos_summary_view" + referencedColumns: ["user_id"] + }, + { + foreignKeyName: "user_details_user_id_fkey" + columns: ["user_id"] + isOneToOne: true referencedRelation: "users" referencedColumns: ["id"] }, { foreignKeyName: "user_details_user_id_fkey" columns: ["user_id"] + isOneToOne: true referencedRelation: "users_view" referencedColumns: ["id"] }, { foreignKeyName: "user_details_user_id_fkey" columns: ["user_id"] + isOneToOne: true referencedRelation: "users_view_with_multiple_refs_to_users" referencedColumns: ["initial_id"] }, { foreignKeyName: "user_details_user_id_fkey" columns: ["user_id"] + isOneToOne: true referencedRelation: "users_view_with_multiple_refs_to_users" referencedColumns: ["second_id"] }, @@ -225,6 +1308,14 @@ test('typegen: typescript', async () => { id: number name: string | null status: Database["public"]["Enums"]["user_status"] | null + test_unnamed_row_composite: + | Database["public"]["CompositeTypes"]["composite_type_with_array_attribute"] + | null + test_unnamed_row_setof: { + details: string | null + id: number + "user-id": number + } | null } Insert: { decimal?: number | null @@ -285,30 +1376,42 @@ test('typegen: typescript', async () => { { foreignKeyName: "todos_user-id_fkey" columns: ["user-id"] + isOneToOne: false referencedRelation: "a_view" referencedColumns: ["id"] }, { foreignKeyName: "todos_user-id_fkey" columns: ["user-id"] + isOneToOne: false + referencedRelation: "user_todos_summary_view" + referencedColumns: ["user_id"] + }, + { + foreignKeyName: "todos_user-id_fkey" + columns: ["user-id"] + isOneToOne: false referencedRelation: "users" referencedColumns: ["id"] }, { foreignKeyName: "todos_user-id_fkey" columns: ["user-id"] + isOneToOne: false referencedRelation: "users_view" referencedColumns: ["id"] }, { foreignKeyName: "todos_user-id_fkey" columns: ["user-id"] + isOneToOne: false referencedRelation: "users_view_with_multiple_refs_to_users" referencedColumns: ["initial_id"] }, { foreignKeyName: "todos_user-id_fkey" columns: ["user-id"] + isOneToOne: false referencedRelation: "users_view_with_multiple_refs_to_users" referencedColumns: ["second_id"] }, @@ -319,6 +1422,12 @@ test('typegen: typescript', async () => { details: string | null id: number | null "user-id": number | null + blurb_varchar: string | null + test_unnamed_view_row: { + details: string | null + id: number + "user-id": number + } | null } Insert: { details?: string | null @@ -334,35 +1443,57 @@ test('typegen: typescript', async () => { { foreignKeyName: "todos_user-id_fkey" columns: ["user-id"] + isOneToOne: false referencedRelation: "a_view" referencedColumns: ["id"] }, { foreignKeyName: "todos_user-id_fkey" columns: ["user-id"] + isOneToOne: false + referencedRelation: "user_todos_summary_view" + referencedColumns: ["user_id"] + }, + { + foreignKeyName: "todos_user-id_fkey" + columns: ["user-id"] + isOneToOne: false referencedRelation: "users" referencedColumns: ["id"] }, { foreignKeyName: "todos_user-id_fkey" columns: ["user-id"] + isOneToOne: false referencedRelation: "users_view" referencedColumns: ["id"] }, { foreignKeyName: "todos_user-id_fkey" columns: ["user-id"] + isOneToOne: false referencedRelation: "users_view_with_multiple_refs_to_users" referencedColumns: ["initial_id"] }, { foreignKeyName: "todos_user-id_fkey" columns: ["user-id"] + isOneToOne: false referencedRelation: "users_view_with_multiple_refs_to_users" referencedColumns: ["second_id"] }, ] } + user_todos_summary_view: { + Row: { + todo_count: number | null + todo_details: string[] | null + user_id: number | null + user_name: string | null + user_status: Database["public"]["Enums"]["user_status"] | null + } + Relationships: [] + } users_view: { Row: { decimal: number | null @@ -397,59 +1528,263 @@ test('typegen: typescript', async () => { Functions: { blurb: { Args: { "": Database["public"]["Tables"]["todos"]["Row"] } - Returns: string - } - blurb_varchar: { - Args: { "": Database["public"]["Tables"]["todos"]["Row"] } - Returns: string + Returns: { + error: true + } & "the function public.blurb with parameter or with a single unnamed json/jsonb parameter, but no matches were found in the schema cache" } + blurb_varchar: + | { + Args: { "": Database["public"]["Tables"]["todos"]["Row"] } + Returns: { + error: true + } & "the function public.blurb_varchar with parameter or with a single unnamed json/jsonb parameter, but no matches were found in the schema cache" + } + | { + Args: { "": Database["public"]["Views"]["todos_view"]["Row"] } + Returns: { + error: true + } & "the function public.blurb_varchar with parameter or with a single unnamed json/jsonb parameter, but no matches were found in the schema cache" + } details_is_long: { Args: { "": Database["public"]["Tables"]["todos"]["Row"] } - Returns: boolean + Returns: { + error: true + } & "the function public.details_is_long with parameter or with a single unnamed json/jsonb parameter, but no matches were found in the schema cache" } details_length: { Args: { "": Database["public"]["Tables"]["todos"]["Row"] } - Returns: number + Returns: { + error: true + } & "the function public.details_length with parameter or with a single unnamed json/jsonb parameter, but no matches were found in the schema cache" } details_words: { Args: { "": Database["public"]["Tables"]["todos"]["Row"] } - Returns: string[] + Returns: { + error: true + } & "the function public.details_words with parameter or with a single unnamed json/jsonb parameter, but no matches were found in the schema cache" } function_returning_row: { - Args: Record + Args: never Returns: { decimal: number | null id: number name: string | null status: Database["public"]["Enums"]["user_status"] | null } + SetofOptions: { + from: "*" + to: "users" + isOneToOne: true + isSetofReturn: false + } } function_returning_set_of_rows: { - Args: Record + Args: never Returns: { decimal: number | null id: number name: string | null status: Database["public"]["Enums"]["user_status"] | null }[] + SetofOptions: { + from: "*" + to: "users" + isOneToOne: false + isSetofReturn: true + } + } + function_returning_single_row: { + Args: { todos: Database["public"]["Tables"]["todos"]["Row"] } + Returns: { + decimal: number | null + id: number + name: string | null + status: Database["public"]["Enums"]["user_status"] | null + } + SetofOptions: { + from: "todos" + to: "users" + isOneToOne: true + isSetofReturn: false + } } function_returning_table: { - Args: Record + Args: never + Returns: { + id: number + name: string + }[] + } + function_returning_table_with_args: { + Args: { user_id: number } Returns: { id: number name: string }[] } - get_todos_setof_rows: { - Args: - | { todo_row: Database["public"]["Tables"]["todos"]["Row"] } - | { user_row: Database["public"]["Tables"]["users"]["Row"] } + function_using_setof_rows_one: { + Args: { user_row: Database["public"]["Tables"]["users"]["Row"] } Returns: { details: string | null id: number "user-id": number - }[] + } + SetofOptions: { + from: "users" + to: "todos" + isOneToOne: true + isSetofReturn: true + } + } + function_using_table_returns: { + Args: { user_row: Database["public"]["Tables"]["users"]["Row"] } + Returns: { + details: string | null + id: number + "user-id": number + } + SetofOptions: { + from: "users" + to: "todos" + isOneToOne: true + isSetofReturn: false + } + } + get_composite_type_data: { + Args: never + Returns: Database["public"]["CompositeTypes"]["composite_type_with_array_attribute"][] + SetofOptions: { + from: "*" + to: "composite_type_with_array_attribute" + isOneToOne: false + isSetofReturn: true + } } + get_single_user_summary_from_view: + | { + Args: { + userview_row: Database["public"]["Views"]["users_view"]["Row"] + } + Returns: { + todo_count: number | null + todo_details: string[] | null + user_id: number | null + user_name: string | null + user_status: Database["public"]["Enums"]["user_status"] | null + } + SetofOptions: { + from: "users_view" + to: "user_todos_summary_view" + isOneToOne: true + isSetofReturn: true + } + } + | { + Args: { user_row: Database["public"]["Tables"]["users"]["Row"] } + Returns: { + todo_count: number | null + todo_details: string[] | null + user_id: number | null + user_name: string | null + user_status: Database["public"]["Enums"]["user_status"] | null + } + SetofOptions: { + from: "users" + to: "user_todos_summary_view" + isOneToOne: true + isSetofReturn: true + } + } + | { + Args: { search_user_id: number } + Returns: { + todo_count: number | null + todo_details: string[] | null + user_id: number | null + user_name: string | null + user_status: Database["public"]["Enums"]["user_status"] | null + } + SetofOptions: { + from: "*" + to: "user_todos_summary_view" + isOneToOne: true + isSetofReturn: true + } + } + get_todos_from_user: + | { + Args: { + userview_row: Database["public"]["Views"]["users_view"]["Row"] + } + Returns: { + details: string | null + id: number + "user-id": number + }[] + SetofOptions: { + from: "users_view" + to: "todos" + isOneToOne: false + isSetofReturn: true + } + } + | { + Args: { user_row: Database["public"]["Tables"]["users"]["Row"] } + Returns: { + details: string | null + id: number + "user-id": number + }[] + SetofOptions: { + from: "users" + to: "todos" + isOneToOne: false + isSetofReturn: true + } + } + | { + Args: { search_user_id: number } + Returns: { + details: string | null + id: number + "user-id": number + }[] + SetofOptions: { + from: "*" + to: "todos" + isOneToOne: false + isSetofReturn: true + } + } + get_todos_setof_rows: + | { + Args: { user_row: Database["public"]["Tables"]["users"]["Row"] } + Returns: { + details: string | null + id: number + "user-id": number + }[] + SetofOptions: { + from: "users" + to: "todos" + isOneToOne: false + isSetofReturn: true + } + } + | { + Args: { todo_row: Database["public"]["Tables"]["todos"]["Row"] } + Returns: { + details: string | null + id: number + "user-id": number + }[] + SetofOptions: { + from: "todos" + to: "todos" + isOneToOne: false + isSetofReturn: true + } + } get_user_audit_setof_single_row: { Args: { user_row: Database["public"]["Tables"]["users"]["Row"] } Returns: { @@ -457,31 +1792,193 @@ test('typegen: typescript', async () => { id: number previous_value: Json | null user_id: number | null - }[] - } - polymorphic_function: { - Args: { "": boolean } | { "": string } - Returns: undefined + } + SetofOptions: { + from: "users" + to: "users_audit" + isOneToOne: true + isSetofReturn: true + } } - postgres_fdw_disconnect: { + get_user_ids: { Args: never; Returns: number[] } + get_user_summary: { Args: never; Returns: Record[] } + polymorphic_function: { Args: { "": string }; Returns: undefined } + polymorphic_function_with_different_return: { Args: { "": string } - Returns: boolean + Returns: string + } + polymorphic_function_with_no_params_or_unnamed: + | { Args: never; Returns: number } + | { Args: { "": string }; Returns: string } + polymorphic_function_with_unnamed_default: + | { + Args: never + Returns: { + error: true + } & "Could not choose the best candidate function between: public.polymorphic_function_with_unnamed_default(), public.polymorphic_function_with_unnamed_default( => text). Try renaming the parameters or the function itself in the database so function overloading can be resolved" + } + | { Args: { ""?: string }; Returns: string } + polymorphic_function_with_unnamed_default_overload: + | { + Args: never + Returns: { + error: true + } & "Could not choose the best candidate function between: public.polymorphic_function_with_unnamed_default_overload(), public.polymorphic_function_with_unnamed_default_overload( => text). Try renaming the parameters or the function itself in the database so function overloading can be resolved" + } + | { Args: { ""?: string }; Returns: string } + polymorphic_function_with_unnamed_json: { + Args: { "": Json } + Returns: number } - postgres_fdw_disconnect_all: { - Args: Record - Returns: boolean + polymorphic_function_with_unnamed_jsonb: { + Args: { "": Json } + Returns: number + } + polymorphic_function_with_unnamed_text: { + Args: { "": string } + Returns: number } + postgres_fdw_disconnect: { Args: { "": string }; Returns: boolean } + postgres_fdw_disconnect_all: { Args: never; Returns: boolean } postgres_fdw_get_connections: { - Args: Record + Args: never Returns: Record[] } - postgres_fdw_handler: { - Args: Record - Returns: unknown + postgres_fdw_handler: { Args: never; Returns: unknown } + postgrest_resolvable_with_override_function: + | { Args: { a: string }; Returns: number } + | { + Args: { user_id: number } + Returns: { + decimal: number | null + id: number + name: string | null + status: Database["public"]["Enums"]["user_status"] | null + }[] + SetofOptions: { + from: "*" + to: "users" + isOneToOne: false + isSetofReturn: true + } + } + | { + Args: { completed: boolean; todo_id: number } + Returns: { + details: string | null + id: number + "user-id": number + }[] + SetofOptions: { + from: "*" + to: "todos" + isOneToOne: false + isSetofReturn: true + } + } + | { + Args: { user_row: Database["public"]["Tables"]["users"]["Row"] } + Returns: { + details: string | null + id: number + "user-id": number + }[] + SetofOptions: { + from: "users" + to: "todos" + isOneToOne: false + isSetofReturn: true + } + } + | { Args: { b: number }; Returns: string } + | { Args: never; Returns: undefined } + postgrest_unresolvable_function: + | { + Args: { a: string } + Returns: { + error: true + } & "Could not choose the best candidate function between: public.postgrest_unresolvable_function(a => int4), public.postgrest_unresolvable_function(a => text). Try renaming the parameters or the function itself in the database so function overloading can be resolved" + } + | { + Args: { a: number } + Returns: { + error: true + } & "Could not choose the best candidate function between: public.postgrest_unresolvable_function(a => int4), public.postgrest_unresolvable_function(a => text). Try renaming the parameters or the function itself in the database so function overloading can be resolved" + } + | { Args: never; Returns: undefined } + test_internal_query: { Args: never; Returns: undefined } + test_unnamed_row_composite: { + Args: { "": Database["public"]["Tables"]["users"]["Row"] } + Returns: Database["public"]["CompositeTypes"]["composite_type_with_array_attribute"] + SetofOptions: { + from: "users" + to: "composite_type_with_array_attribute" + isOneToOne: true + isSetofReturn: false + } + } + test_unnamed_row_scalar: { + Args: { "": Database["public"]["Tables"]["todos"]["Row"] } + Returns: { + error: true + } & "the function public.test_unnamed_row_scalar with parameter or with a single unnamed json/jsonb parameter, but no matches were found in the schema cache" } - test_internal_query: { - Args: Record - Returns: undefined + test_unnamed_row_setof: + | { + Args: { user_id: number } + Returns: { + details: string | null + id: number + "user-id": number + }[] + SetofOptions: { + from: "*" + to: "todos" + isOneToOne: false + isSetofReturn: true + } + } + | { + Args: { "": Database["public"]["Tables"]["todos"]["Row"] } + Returns: { + details: string | null + id: number + "user-id": number + }[] + SetofOptions: { + from: "todos" + to: "todos" + isOneToOne: false + isSetofReturn: true + } + } + | { + Args: { "": Database["public"]["Tables"]["users"]["Row"] } + Returns: { + details: string | null + id: number + "user-id": number + }[] + SetofOptions: { + from: "users" + to: "todos" + isOneToOne: false + isSetofReturn: true + } + } + test_unnamed_view_row: { + Args: { "": Database["public"]["Views"]["todos_view"]["Row"] } + Returns: { + details: string | null + id: number + "user-id": number + }[] + SetofOptions: { + from: "todos_view" + to: "todos" + isOneToOne: false + isSetofReturn: true + } } } Enums: { @@ -762,6 +2259,12 @@ test('typegen: typescript w/ one-to-one relationships', async () => { details_is_long: boolean | null details_length: number | null details_words: string[] | null + test_unnamed_row_scalar: number | null + test_unnamed_row_setof: { + details: string | null + id: number + "user-id": number + } | null } Insert: { details?: string | null @@ -781,6 +2284,13 @@ test('typegen: typescript w/ one-to-one relationships', async () => { referencedRelation: "a_view" referencedColumns: ["id"] }, + { + foreignKeyName: "todos_user-id_fkey" + columns: ["user-id"] + isOneToOne: false + referencedRelation: "user_todos_summary_view" + referencedColumns: ["user_id"] + }, { foreignKeyName: "todos_user-id_fkey" columns: ["user-id"] @@ -832,6 +2342,13 @@ test('typegen: typescript w/ one-to-one relationships', async () => { referencedRelation: "a_view" referencedColumns: ["id"] }, + { + foreignKeyName: "user_details_user_id_fkey" + columns: ["user_id"] + isOneToOne: true + referencedRelation: "user_todos_summary_view" + referencedColumns: ["user_id"] + }, { foreignKeyName: "user_details_user_id_fkey" columns: ["user_id"] @@ -868,6 +2385,14 @@ test('typegen: typescript w/ one-to-one relationships', async () => { id: number name: string | null status: Database["public"]["Enums"]["user_status"] | null + test_unnamed_row_composite: + | Database["public"]["CompositeTypes"]["composite_type_with_array_attribute"] + | null + test_unnamed_row_setof: { + details: string | null + id: number + "user-id": number + } | null } Insert: { decimal?: number | null @@ -932,6 +2457,13 @@ test('typegen: typescript w/ one-to-one relationships', async () => { referencedRelation: "a_view" referencedColumns: ["id"] }, + { + foreignKeyName: "todos_user-id_fkey" + columns: ["user-id"] + isOneToOne: false + referencedRelation: "user_todos_summary_view" + referencedColumns: ["user_id"] + }, { foreignKeyName: "todos_user-id_fkey" columns: ["user-id"] @@ -967,6 +2499,12 @@ test('typegen: typescript w/ one-to-one relationships', async () => { details: string | null id: number | null "user-id": number | null + blurb_varchar: string | null + test_unnamed_view_row: { + details: string | null + id: number + "user-id": number + } | null } Insert: { details?: string | null @@ -986,6 +2524,13 @@ test('typegen: typescript w/ one-to-one relationships', async () => { referencedRelation: "a_view" referencedColumns: ["id"] }, + { + foreignKeyName: "todos_user-id_fkey" + columns: ["user-id"] + isOneToOne: false + referencedRelation: "user_todos_summary_view" + referencedColumns: ["user_id"] + }, { foreignKeyName: "todos_user-id_fkey" columns: ["user-id"] @@ -1016,6 +2561,16 @@ test('typegen: typescript w/ one-to-one relationships', async () => { }, ] } + user_todos_summary_view: { + Row: { + todo_count: number | null + todo_details: string[] | null + user_id: number | null + user_name: string | null + user_status: Database["public"]["Enums"]["user_status"] | null + } + Relationships: [] + } users_view: { Row: { decimal: number | null @@ -1050,59 +2605,263 @@ test('typegen: typescript w/ one-to-one relationships', async () => { Functions: { blurb: { Args: { "": Database["public"]["Tables"]["todos"]["Row"] } - Returns: string - } - blurb_varchar: { - Args: { "": Database["public"]["Tables"]["todos"]["Row"] } - Returns: string + Returns: { + error: true + } & "the function public.blurb with parameter or with a single unnamed json/jsonb parameter, but no matches were found in the schema cache" } + blurb_varchar: + | { + Args: { "": Database["public"]["Tables"]["todos"]["Row"] } + Returns: { + error: true + } & "the function public.blurb_varchar with parameter or with a single unnamed json/jsonb parameter, but no matches were found in the schema cache" + } + | { + Args: { "": Database["public"]["Views"]["todos_view"]["Row"] } + Returns: { + error: true + } & "the function public.blurb_varchar with parameter or with a single unnamed json/jsonb parameter, but no matches were found in the schema cache" + } details_is_long: { Args: { "": Database["public"]["Tables"]["todos"]["Row"] } - Returns: boolean + Returns: { + error: true + } & "the function public.details_is_long with parameter or with a single unnamed json/jsonb parameter, but no matches were found in the schema cache" } details_length: { Args: { "": Database["public"]["Tables"]["todos"]["Row"] } - Returns: number + Returns: { + error: true + } & "the function public.details_length with parameter or with a single unnamed json/jsonb parameter, but no matches were found in the schema cache" } details_words: { Args: { "": Database["public"]["Tables"]["todos"]["Row"] } - Returns: string[] + Returns: { + error: true + } & "the function public.details_words with parameter or with a single unnamed json/jsonb parameter, but no matches were found in the schema cache" } function_returning_row: { - Args: Record + Args: never Returns: { decimal: number | null id: number name: string | null status: Database["public"]["Enums"]["user_status"] | null } + SetofOptions: { + from: "*" + to: "users" + isOneToOne: true + isSetofReturn: false + } } function_returning_set_of_rows: { - Args: Record + Args: never Returns: { decimal: number | null id: number name: string | null status: Database["public"]["Enums"]["user_status"] | null }[] + SetofOptions: { + from: "*" + to: "users" + isOneToOne: false + isSetofReturn: true + } + } + function_returning_single_row: { + Args: { todos: Database["public"]["Tables"]["todos"]["Row"] } + Returns: { + decimal: number | null + id: number + name: string | null + status: Database["public"]["Enums"]["user_status"] | null + } + SetofOptions: { + from: "todos" + to: "users" + isOneToOne: true + isSetofReturn: false + } } function_returning_table: { - Args: Record + Args: never + Returns: { + id: number + name: string + }[] + } + function_returning_table_with_args: { + Args: { user_id: number } Returns: { id: number name: string }[] } - get_todos_setof_rows: { - Args: - | { todo_row: Database["public"]["Tables"]["todos"]["Row"] } - | { user_row: Database["public"]["Tables"]["users"]["Row"] } + function_using_setof_rows_one: { + Args: { user_row: Database["public"]["Tables"]["users"]["Row"] } Returns: { details: string | null id: number "user-id": number - }[] + } + SetofOptions: { + from: "users" + to: "todos" + isOneToOne: true + isSetofReturn: true + } + } + function_using_table_returns: { + Args: { user_row: Database["public"]["Tables"]["users"]["Row"] } + Returns: { + details: string | null + id: number + "user-id": number + } + SetofOptions: { + from: "users" + to: "todos" + isOneToOne: true + isSetofReturn: false + } } + get_composite_type_data: { + Args: never + Returns: Database["public"]["CompositeTypes"]["composite_type_with_array_attribute"][] + SetofOptions: { + from: "*" + to: "composite_type_with_array_attribute" + isOneToOne: false + isSetofReturn: true + } + } + get_single_user_summary_from_view: + | { + Args: { + userview_row: Database["public"]["Views"]["users_view"]["Row"] + } + Returns: { + todo_count: number | null + todo_details: string[] | null + user_id: number | null + user_name: string | null + user_status: Database["public"]["Enums"]["user_status"] | null + } + SetofOptions: { + from: "users_view" + to: "user_todos_summary_view" + isOneToOne: true + isSetofReturn: true + } + } + | { + Args: { user_row: Database["public"]["Tables"]["users"]["Row"] } + Returns: { + todo_count: number | null + todo_details: string[] | null + user_id: number | null + user_name: string | null + user_status: Database["public"]["Enums"]["user_status"] | null + } + SetofOptions: { + from: "users" + to: "user_todos_summary_view" + isOneToOne: true + isSetofReturn: true + } + } + | { + Args: { search_user_id: number } + Returns: { + todo_count: number | null + todo_details: string[] | null + user_id: number | null + user_name: string | null + user_status: Database["public"]["Enums"]["user_status"] | null + } + SetofOptions: { + from: "*" + to: "user_todos_summary_view" + isOneToOne: true + isSetofReturn: true + } + } + get_todos_from_user: + | { + Args: { + userview_row: Database["public"]["Views"]["users_view"]["Row"] + } + Returns: { + details: string | null + id: number + "user-id": number + }[] + SetofOptions: { + from: "users_view" + to: "todos" + isOneToOne: false + isSetofReturn: true + } + } + | { + Args: { user_row: Database["public"]["Tables"]["users"]["Row"] } + Returns: { + details: string | null + id: number + "user-id": number + }[] + SetofOptions: { + from: "users" + to: "todos" + isOneToOne: false + isSetofReturn: true + } + } + | { + Args: { search_user_id: number } + Returns: { + details: string | null + id: number + "user-id": number + }[] + SetofOptions: { + from: "*" + to: "todos" + isOneToOne: false + isSetofReturn: true + } + } + get_todos_setof_rows: + | { + Args: { user_row: Database["public"]["Tables"]["users"]["Row"] } + Returns: { + details: string | null + id: number + "user-id": number + }[] + SetofOptions: { + from: "users" + to: "todos" + isOneToOne: false + isSetofReturn: true + } + } + | { + Args: { todo_row: Database["public"]["Tables"]["todos"]["Row"] } + Returns: { + details: string | null + id: number + "user-id": number + }[] + SetofOptions: { + from: "todos" + to: "todos" + isOneToOne: false + isSetofReturn: true + } + } get_user_audit_setof_single_row: { Args: { user_row: Database["public"]["Tables"]["users"]["Row"] } Returns: { @@ -1110,31 +2869,193 @@ test('typegen: typescript w/ one-to-one relationships', async () => { id: number previous_value: Json | null user_id: number | null - }[] - } - polymorphic_function: { - Args: { "": boolean } | { "": string } - Returns: undefined + } + SetofOptions: { + from: "users" + to: "users_audit" + isOneToOne: true + isSetofReturn: true + } } - postgres_fdw_disconnect: { + get_user_ids: { Args: never; Returns: number[] } + get_user_summary: { Args: never; Returns: Record[] } + polymorphic_function: { Args: { "": string }; Returns: undefined } + polymorphic_function_with_different_return: { Args: { "": string } - Returns: boolean + Returns: string + } + polymorphic_function_with_no_params_or_unnamed: + | { Args: never; Returns: number } + | { Args: { "": string }; Returns: string } + polymorphic_function_with_unnamed_default: + | { + Args: never + Returns: { + error: true + } & "Could not choose the best candidate function between: public.polymorphic_function_with_unnamed_default(), public.polymorphic_function_with_unnamed_default( => text). Try renaming the parameters or the function itself in the database so function overloading can be resolved" + } + | { Args: { ""?: string }; Returns: string } + polymorphic_function_with_unnamed_default_overload: + | { + Args: never + Returns: { + error: true + } & "Could not choose the best candidate function between: public.polymorphic_function_with_unnamed_default_overload(), public.polymorphic_function_with_unnamed_default_overload( => text). Try renaming the parameters or the function itself in the database so function overloading can be resolved" + } + | { Args: { ""?: string }; Returns: string } + polymorphic_function_with_unnamed_json: { + Args: { "": Json } + Returns: number + } + polymorphic_function_with_unnamed_jsonb: { + Args: { "": Json } + Returns: number } - postgres_fdw_disconnect_all: { - Args: Record - Returns: boolean + polymorphic_function_with_unnamed_text: { + Args: { "": string } + Returns: number } + postgres_fdw_disconnect: { Args: { "": string }; Returns: boolean } + postgres_fdw_disconnect_all: { Args: never; Returns: boolean } postgres_fdw_get_connections: { - Args: Record + Args: never Returns: Record[] } - postgres_fdw_handler: { - Args: Record - Returns: unknown + postgres_fdw_handler: { Args: never; Returns: unknown } + postgrest_resolvable_with_override_function: + | { Args: { a: string }; Returns: number } + | { + Args: { user_id: number } + Returns: { + decimal: number | null + id: number + name: string | null + status: Database["public"]["Enums"]["user_status"] | null + }[] + SetofOptions: { + from: "*" + to: "users" + isOneToOne: false + isSetofReturn: true + } + } + | { + Args: { completed: boolean; todo_id: number } + Returns: { + details: string | null + id: number + "user-id": number + }[] + SetofOptions: { + from: "*" + to: "todos" + isOneToOne: false + isSetofReturn: true + } + } + | { + Args: { user_row: Database["public"]["Tables"]["users"]["Row"] } + Returns: { + details: string | null + id: number + "user-id": number + }[] + SetofOptions: { + from: "users" + to: "todos" + isOneToOne: false + isSetofReturn: true + } + } + | { Args: { b: number }; Returns: string } + | { Args: never; Returns: undefined } + postgrest_unresolvable_function: + | { + Args: { a: string } + Returns: { + error: true + } & "Could not choose the best candidate function between: public.postgrest_unresolvable_function(a => int4), public.postgrest_unresolvable_function(a => text). Try renaming the parameters or the function itself in the database so function overloading can be resolved" + } + | { + Args: { a: number } + Returns: { + error: true + } & "Could not choose the best candidate function between: public.postgrest_unresolvable_function(a => int4), public.postgrest_unresolvable_function(a => text). Try renaming the parameters or the function itself in the database so function overloading can be resolved" + } + | { Args: never; Returns: undefined } + test_internal_query: { Args: never; Returns: undefined } + test_unnamed_row_composite: { + Args: { "": Database["public"]["Tables"]["users"]["Row"] } + Returns: Database["public"]["CompositeTypes"]["composite_type_with_array_attribute"] + SetofOptions: { + from: "users" + to: "composite_type_with_array_attribute" + isOneToOne: true + isSetofReturn: false + } + } + test_unnamed_row_scalar: { + Args: { "": Database["public"]["Tables"]["todos"]["Row"] } + Returns: { + error: true + } & "the function public.test_unnamed_row_scalar with parameter or with a single unnamed json/jsonb parameter, but no matches were found in the schema cache" } - test_internal_query: { - Args: Record - Returns: undefined + test_unnamed_row_setof: + | { + Args: { user_id: number } + Returns: { + details: string | null + id: number + "user-id": number + }[] + SetofOptions: { + from: "*" + to: "todos" + isOneToOne: false + isSetofReturn: true + } + } + | { + Args: { "": Database["public"]["Tables"]["todos"]["Row"] } + Returns: { + details: string | null + id: number + "user-id": number + }[] + SetofOptions: { + from: "todos" + to: "todos" + isOneToOne: false + isSetofReturn: true + } + } + | { + Args: { "": Database["public"]["Tables"]["users"]["Row"] } + Returns: { + details: string | null + id: number + "user-id": number + }[] + SetofOptions: { + from: "users" + to: "todos" + isOneToOne: false + isSetofReturn: true + } + } + test_unnamed_view_row: { + Args: { "": Database["public"]["Views"]["todos_view"]["Row"] } + Returns: { + details: string | null + id: number + "user-id": number + }[] + SetofOptions: { + from: "todos_view" + to: "todos" + isOneToOne: false + isSetofReturn: true + } } } Enums: { @@ -1420,6 +3341,12 @@ test('typegen: typescript w/ postgrestVersion', async () => { details_is_long: boolean | null details_length: number | null details_words: string[] | null + test_unnamed_row_scalar: number | null + test_unnamed_row_setof: { + details: string | null + id: number + "user-id": number + } | null } Insert: { details?: string | null @@ -1439,6 +3366,13 @@ test('typegen: typescript w/ postgrestVersion', async () => { referencedRelation: "a_view" referencedColumns: ["id"] }, + { + foreignKeyName: "todos_user-id_fkey" + columns: ["user-id"] + isOneToOne: false + referencedRelation: "user_todos_summary_view" + referencedColumns: ["user_id"] + }, { foreignKeyName: "todos_user-id_fkey" columns: ["user-id"] @@ -1490,6 +3424,13 @@ test('typegen: typescript w/ postgrestVersion', async () => { referencedRelation: "a_view" referencedColumns: ["id"] }, + { + foreignKeyName: "user_details_user_id_fkey" + columns: ["user_id"] + isOneToOne: true + referencedRelation: "user_todos_summary_view" + referencedColumns: ["user_id"] + }, { foreignKeyName: "user_details_user_id_fkey" columns: ["user_id"] @@ -1526,6 +3467,14 @@ test('typegen: typescript w/ postgrestVersion', async () => { id: number name: string | null status: Database["public"]["Enums"]["user_status"] | null + test_unnamed_row_composite: + | Database["public"]["CompositeTypes"]["composite_type_with_array_attribute"] + | null + test_unnamed_row_setof: { + details: string | null + id: number + "user-id": number + } | null } Insert: { decimal?: number | null @@ -1590,6 +3539,13 @@ test('typegen: typescript w/ postgrestVersion', async () => { referencedRelation: "a_view" referencedColumns: ["id"] }, + { + foreignKeyName: "todos_user-id_fkey" + columns: ["user-id"] + isOneToOne: false + referencedRelation: "user_todos_summary_view" + referencedColumns: ["user_id"] + }, { foreignKeyName: "todos_user-id_fkey" columns: ["user-id"] @@ -1625,6 +3581,12 @@ test('typegen: typescript w/ postgrestVersion', async () => { details: string | null id: number | null "user-id": number | null + blurb_varchar: string | null + test_unnamed_view_row: { + details: string | null + id: number + "user-id": number + } | null } Insert: { details?: string | null @@ -1644,6 +3606,13 @@ test('typegen: typescript w/ postgrestVersion', async () => { referencedRelation: "a_view" referencedColumns: ["id"] }, + { + foreignKeyName: "todos_user-id_fkey" + columns: ["user-id"] + isOneToOne: false + referencedRelation: "user_todos_summary_view" + referencedColumns: ["user_id"] + }, { foreignKeyName: "todos_user-id_fkey" columns: ["user-id"] @@ -1674,6 +3643,16 @@ test('typegen: typescript w/ postgrestVersion', async () => { }, ] } + user_todos_summary_view: { + Row: { + todo_count: number | null + todo_details: string[] | null + user_id: number | null + user_name: string | null + user_status: Database["public"]["Enums"]["user_status"] | null + } + Relationships: [] + } users_view: { Row: { decimal: number | null @@ -1708,59 +3687,263 @@ test('typegen: typescript w/ postgrestVersion', async () => { Functions: { blurb: { Args: { "": Database["public"]["Tables"]["todos"]["Row"] } - Returns: string - } - blurb_varchar: { - Args: { "": Database["public"]["Tables"]["todos"]["Row"] } - Returns: string + Returns: { + error: true + } & "the function public.blurb with parameter or with a single unnamed json/jsonb parameter, but no matches were found in the schema cache" } + blurb_varchar: + | { + Args: { "": Database["public"]["Tables"]["todos"]["Row"] } + Returns: { + error: true + } & "the function public.blurb_varchar with parameter or with a single unnamed json/jsonb parameter, but no matches were found in the schema cache" + } + | { + Args: { "": Database["public"]["Views"]["todos_view"]["Row"] } + Returns: { + error: true + } & "the function public.blurb_varchar with parameter or with a single unnamed json/jsonb parameter, but no matches were found in the schema cache" + } details_is_long: { Args: { "": Database["public"]["Tables"]["todos"]["Row"] } - Returns: boolean + Returns: { + error: true + } & "the function public.details_is_long with parameter or with a single unnamed json/jsonb parameter, but no matches were found in the schema cache" } details_length: { Args: { "": Database["public"]["Tables"]["todos"]["Row"] } - Returns: number + Returns: { + error: true + } & "the function public.details_length with parameter or with a single unnamed json/jsonb parameter, but no matches were found in the schema cache" } details_words: { Args: { "": Database["public"]["Tables"]["todos"]["Row"] } - Returns: string[] + Returns: { + error: true + } & "the function public.details_words with parameter or with a single unnamed json/jsonb parameter, but no matches were found in the schema cache" } function_returning_row: { - Args: Record + Args: never Returns: { decimal: number | null id: number name: string | null status: Database["public"]["Enums"]["user_status"] | null } + SetofOptions: { + from: "*" + to: "users" + isOneToOne: true + isSetofReturn: false + } } function_returning_set_of_rows: { - Args: Record + Args: never Returns: { decimal: number | null id: number name: string | null status: Database["public"]["Enums"]["user_status"] | null }[] + SetofOptions: { + from: "*" + to: "users" + isOneToOne: false + isSetofReturn: true + } + } + function_returning_single_row: { + Args: { todos: Database["public"]["Tables"]["todos"]["Row"] } + Returns: { + decimal: number | null + id: number + name: string | null + status: Database["public"]["Enums"]["user_status"] | null + } + SetofOptions: { + from: "todos" + to: "users" + isOneToOne: true + isSetofReturn: false + } } function_returning_table: { - Args: Record + Args: never Returns: { id: number name: string }[] } - get_todos_setof_rows: { - Args: - | { todo_row: Database["public"]["Tables"]["todos"]["Row"] } - | { user_row: Database["public"]["Tables"]["users"]["Row"] } + function_returning_table_with_args: { + Args: { user_id: number } + Returns: { + id: number + name: string + }[] + } + function_using_setof_rows_one: { + Args: { user_row: Database["public"]["Tables"]["users"]["Row"] } Returns: { details: string | null id: number "user-id": number - }[] + } + SetofOptions: { + from: "users" + to: "todos" + isOneToOne: true + isSetofReturn: true + } + } + function_using_table_returns: { + Args: { user_row: Database["public"]["Tables"]["users"]["Row"] } + Returns: { + details: string | null + id: number + "user-id": number + } + SetofOptions: { + from: "users" + to: "todos" + isOneToOne: true + isSetofReturn: false + } + } + get_composite_type_data: { + Args: never + Returns: Database["public"]["CompositeTypes"]["composite_type_with_array_attribute"][] + SetofOptions: { + from: "*" + to: "composite_type_with_array_attribute" + isOneToOne: false + isSetofReturn: true + } } + get_single_user_summary_from_view: + | { + Args: { + userview_row: Database["public"]["Views"]["users_view"]["Row"] + } + Returns: { + todo_count: number | null + todo_details: string[] | null + user_id: number | null + user_name: string | null + user_status: Database["public"]["Enums"]["user_status"] | null + } + SetofOptions: { + from: "users_view" + to: "user_todos_summary_view" + isOneToOne: true + isSetofReturn: true + } + } + | { + Args: { user_row: Database["public"]["Tables"]["users"]["Row"] } + Returns: { + todo_count: number | null + todo_details: string[] | null + user_id: number | null + user_name: string | null + user_status: Database["public"]["Enums"]["user_status"] | null + } + SetofOptions: { + from: "users" + to: "user_todos_summary_view" + isOneToOne: true + isSetofReturn: true + } + } + | { + Args: { search_user_id: number } + Returns: { + todo_count: number | null + todo_details: string[] | null + user_id: number | null + user_name: string | null + user_status: Database["public"]["Enums"]["user_status"] | null + } + SetofOptions: { + from: "*" + to: "user_todos_summary_view" + isOneToOne: true + isSetofReturn: true + } + } + get_todos_from_user: + | { + Args: { + userview_row: Database["public"]["Views"]["users_view"]["Row"] + } + Returns: { + details: string | null + id: number + "user-id": number + }[] + SetofOptions: { + from: "users_view" + to: "todos" + isOneToOne: false + isSetofReturn: true + } + } + | { + Args: { user_row: Database["public"]["Tables"]["users"]["Row"] } + Returns: { + details: string | null + id: number + "user-id": number + }[] + SetofOptions: { + from: "users" + to: "todos" + isOneToOne: false + isSetofReturn: true + } + } + | { + Args: { search_user_id: number } + Returns: { + details: string | null + id: number + "user-id": number + }[] + SetofOptions: { + from: "*" + to: "todos" + isOneToOne: false + isSetofReturn: true + } + } + get_todos_setof_rows: + | { + Args: { user_row: Database["public"]["Tables"]["users"]["Row"] } + Returns: { + details: string | null + id: number + "user-id": number + }[] + SetofOptions: { + from: "users" + to: "todos" + isOneToOne: false + isSetofReturn: true + } + } + | { + Args: { todo_row: Database["public"]["Tables"]["todos"]["Row"] } + Returns: { + details: string | null + id: number + "user-id": number + }[] + SetofOptions: { + from: "todos" + to: "todos" + isOneToOne: false + isSetofReturn: true + } + } get_user_audit_setof_single_row: { Args: { user_row: Database["public"]["Tables"]["users"]["Row"] } Returns: { @@ -1768,31 +3951,193 @@ test('typegen: typescript w/ postgrestVersion', async () => { id: number previous_value: Json | null user_id: number | null - }[] - } - polymorphic_function: { - Args: { "": boolean } | { "": string } - Returns: undefined + } + SetofOptions: { + from: "users" + to: "users_audit" + isOneToOne: true + isSetofReturn: true + } } - postgres_fdw_disconnect: { + get_user_ids: { Args: never; Returns: number[] } + get_user_summary: { Args: never; Returns: Record[] } + polymorphic_function: { Args: { "": string }; Returns: undefined } + polymorphic_function_with_different_return: { Args: { "": string } - Returns: boolean + Returns: string } - postgres_fdw_disconnect_all: { - Args: Record - Returns: boolean + polymorphic_function_with_no_params_or_unnamed: + | { Args: never; Returns: number } + | { Args: { "": string }; Returns: string } + polymorphic_function_with_unnamed_default: + | { + Args: never + Returns: { + error: true + } & "Could not choose the best candidate function between: public.polymorphic_function_with_unnamed_default(), public.polymorphic_function_with_unnamed_default( => text). Try renaming the parameters or the function itself in the database so function overloading can be resolved" + } + | { Args: { ""?: string }; Returns: string } + polymorphic_function_with_unnamed_default_overload: + | { + Args: never + Returns: { + error: true + } & "Could not choose the best candidate function between: public.polymorphic_function_with_unnamed_default_overload(), public.polymorphic_function_with_unnamed_default_overload( => text). Try renaming the parameters or the function itself in the database so function overloading can be resolved" + } + | { Args: { ""?: string }; Returns: string } + polymorphic_function_with_unnamed_json: { + Args: { "": Json } + Returns: number + } + polymorphic_function_with_unnamed_jsonb: { + Args: { "": Json } + Returns: number + } + polymorphic_function_with_unnamed_text: { + Args: { "": string } + Returns: number } + postgres_fdw_disconnect: { Args: { "": string }; Returns: boolean } + postgres_fdw_disconnect_all: { Args: never; Returns: boolean } postgres_fdw_get_connections: { - Args: Record + Args: never Returns: Record[] } - postgres_fdw_handler: { - Args: Record - Returns: unknown + postgres_fdw_handler: { Args: never; Returns: unknown } + postgrest_resolvable_with_override_function: + | { Args: { a: string }; Returns: number } + | { + Args: { user_id: number } + Returns: { + decimal: number | null + id: number + name: string | null + status: Database["public"]["Enums"]["user_status"] | null + }[] + SetofOptions: { + from: "*" + to: "users" + isOneToOne: false + isSetofReturn: true + } + } + | { + Args: { completed: boolean; todo_id: number } + Returns: { + details: string | null + id: number + "user-id": number + }[] + SetofOptions: { + from: "*" + to: "todos" + isOneToOne: false + isSetofReturn: true + } + } + | { + Args: { user_row: Database["public"]["Tables"]["users"]["Row"] } + Returns: { + details: string | null + id: number + "user-id": number + }[] + SetofOptions: { + from: "users" + to: "todos" + isOneToOne: false + isSetofReturn: true + } + } + | { Args: { b: number }; Returns: string } + | { Args: never; Returns: undefined } + postgrest_unresolvable_function: + | { + Args: { a: string } + Returns: { + error: true + } & "Could not choose the best candidate function between: public.postgrest_unresolvable_function(a => int4), public.postgrest_unresolvable_function(a => text). Try renaming the parameters or the function itself in the database so function overloading can be resolved" + } + | { + Args: { a: number } + Returns: { + error: true + } & "Could not choose the best candidate function between: public.postgrest_unresolvable_function(a => int4), public.postgrest_unresolvable_function(a => text). Try renaming the parameters or the function itself in the database so function overloading can be resolved" + } + | { Args: never; Returns: undefined } + test_internal_query: { Args: never; Returns: undefined } + test_unnamed_row_composite: { + Args: { "": Database["public"]["Tables"]["users"]["Row"] } + Returns: Database["public"]["CompositeTypes"]["composite_type_with_array_attribute"] + SetofOptions: { + from: "users" + to: "composite_type_with_array_attribute" + isOneToOne: true + isSetofReturn: false + } + } + test_unnamed_row_scalar: { + Args: { "": Database["public"]["Tables"]["todos"]["Row"] } + Returns: { + error: true + } & "the function public.test_unnamed_row_scalar with parameter or with a single unnamed json/jsonb parameter, but no matches were found in the schema cache" } - test_internal_query: { - Args: Record - Returns: undefined + test_unnamed_row_setof: + | { + Args: { user_id: number } + Returns: { + details: string | null + id: number + "user-id": number + }[] + SetofOptions: { + from: "*" + to: "todos" + isOneToOne: false + isSetofReturn: true + } + } + | { + Args: { "": Database["public"]["Tables"]["todos"]["Row"] } + Returns: { + details: string | null + id: number + "user-id": number + }[] + SetofOptions: { + from: "todos" + to: "todos" + isOneToOne: false + isSetofReturn: true + } + } + | { + Args: { "": Database["public"]["Tables"]["users"]["Row"] } + Returns: { + details: string | null + id: number + "user-id": number + }[] + SetofOptions: { + from: "users" + to: "todos" + isOneToOne: false + isSetofReturn: true + } + } + test_unnamed_view_row: { + Args: { "": Database["public"]["Views"]["todos_view"]["Row"] } + Returns: { + details: string | null + id: number + "user-id": number + }[] + SetofOptions: { + from: "todos_view" + to: "todos" + isOneToOne: false + isSetofReturn: true + } } } Enums: { @@ -2371,6 +4716,10 @@ test('typegen: go', async () => { Status *string \`json:"status"\` } + type PublicAViewSelect struct { + Id *int64 \`json:"id"\` + } + type PublicTodosViewSelect struct { Details *string \`json:"details"\` Id *int64 \`json:"id"\` @@ -2384,8 +4733,12 @@ test('typegen: go', async () => { Status *string \`json:"status"\` } - type PublicAViewSelect struct { - Id *int64 \`json:"id"\` + type PublicUserTodosSummaryViewSelect struct { + TodoCount *int64 \`json:"todo_count"\` + TodoDetails []*string \`json:"todo_details"\` + UserId *int64 \`json:"user_id"\` + UserName *string \`json:"user_name"\` + UserStatus *string \`json:"user_status"\` } type PublicUsersViewWithMultipleRefsToUsersSelect struct { @@ -2738,6 +5091,20 @@ test('typegen: swift', async () => { case userId = "user-id" } } + internal struct UserTodosSummaryViewSelect: Codable, Hashable, Sendable { + internal let todoCount: Int64? + internal let todoDetails: [String]? + internal let userId: Int64? + internal let userName: String? + internal let userStatus: UserStatus? + internal enum CodingKeys: String, CodingKey { + case todoCount = "todo_count" + case todoDetails = "todo_details" + case userId = "user_id" + case userName = "user_name" + case userStatus = "user_status" + } + } internal struct UsersViewSelect: Codable, Hashable, Sendable { internal let decimal: Decimal? internal let id: Int64? @@ -3109,6 +5476,20 @@ test('typegen: swift w/ public access control', async () => { case userId = "user-id" } } + public struct UserTodosSummaryViewSelect: Codable, Hashable, Sendable { + public let todoCount: Int64? + public let todoDetails: [String]? + public let userId: Int64? + public let userName: String? + public let userStatus: UserStatus? + public enum CodingKeys: String, CodingKey { + case todoCount = "todo_count" + case todoDetails = "todo_details" + case userId = "user_id" + case userName = "user_name" + case userStatus = "user_status" + } + } public struct UsersViewSelect: Codable, Hashable, Sendable { public let decimal: Decimal? public let id: Int64?