Skip to content

Commit

Permalink
some cleanup + restore columns
Browse files Browse the repository at this point in the history
  • Loading branch information
mmkal committed Sep 5, 2024
1 parent f852cfe commit 5f987f6
Show file tree
Hide file tree
Showing 4 changed files with 88 additions and 40 deletions.
58 changes: 31 additions & 27 deletions packages/typegen/src/query/analyze-select-statement.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ export const analyzeSelectStatement = async (
.slice(0, 6)
const schemaIdentifier = sql.identifier([schemaName])

await client.query(sql`create schema if not exists ${schemaIdentifier}`)
return client.transaction(async tx => {
await tx.query(sql`create schema if not exists ${schemaIdentifier}`)
await tx.query(sql`
set search_path to ${schemaIdentifier}, ${sql.raw(await tx.oneFirst<{search_path: string}>(sql`show search_path`))};
`)
Expand All @@ -40,30 +40,38 @@ export const analyzeSelectStatement = async (
const ast = parse(modifiedAST.originalSql)[0]
if (ast.type !== 'with') throw new Error('Expected a WITH clause, got ' + toSql.statement(ast))

for (const {statement, alias} of ast.bind) {
for (const {statement, alias: tableAlias} of ast.bind) {
const modifiedAst = getASTModifiedToSingleSelect(toSql.statement(statement))
const analyzed = await analyzeSelectStatement(tx, modifiedAst)

const statementAliasInfo = aliasMappings(statement)
const aliasList = analyzed[0].column_aliases

const tempTableColumns = aliasList.map(aliasName => {
const found = statementAliasInfo.find(info => info.queryColumn === aliasName)
if (!found) throw new Error(`Alias ${aliasName} not found in statement`)

const analyzedResult = analyzed.find(a => a.table_column_name === found.aliasFor)
if (!analyzedResult) throw new Error(`Alias ${aliasName} not found in analyzed results`)

const def = `${aliasName} ${analyzedResult.underlying_data_type} ${analyzedResult.is_underlying_nullable === 'NO' ? 'not null' : ''}`

const comment = `From CTE expression "${tableAlias.name}", column source: ${analyzedResult.schema_name}.${analyzedResult.underlying_table_name}.${analyzedResult.table_column_name}`
return {
name: aliasName,
def,
comment,
}
})

const raw = sql.raw(`
drop table if exists ${schemaName}.${alias.name};
create table ${schemaName}.${alias.name}(
${aliasList
.map(aliasName => {
const found = statementAliasInfo.find(info => info.queryColumn === aliasName)
if (!found) throw new Error(`Alias ${aliasName} not found in statement`)
const analyzedResult = analyzed.find(a => a.table_column_name === found.aliasFor)
if (!analyzedResult) throw new Error(`Alias ${aliasName} not found in analyzed results`)
return `${aliasName} ${analyzedResult.underlying_data_type} ${analyzedResult.is_underlying_nullable === 'NO' ? 'not null' : ''}`
})
.join(',\n')}
)
drop table if exists ${schemaName}.${tableAlias.name};
create table ${schemaName}.${tableAlias.name}(
${tempTableColumns.map(c => c.def).join(',\n')}
);
${tempTableColumns.map(c => `comment on column ${schemaName}.${tableAlias.name}.${c.name} is '${c.comment}';`).join('\n')}
`)
// ${analyzed.map((a, i) => `${a.table_column_name} ${a.underlying_data_type} ${a.is_underlying_nullable === 'NO' ? 'not null' : ''}`).join(',\n')}
console.log('create table statement::::', raw)
await tx.query(raw)
}
return analyzeSelectStatement(tx, getASTModifiedToSingleSelect(toSql.statement(ast.in)))
Expand All @@ -88,24 +96,17 @@ export const analyzeSelectStatement = async (

const results = SelectStatementAnalyzedColumnSchema.array().parse(rows)

console.log(
`select * from ${[schemaName, 'analyze_select_statement_columns'].join('.')}('${selectStatementSql}')`,
{
results,
},
)

const deduped = lodash.uniqBy<SelectStatementAnalyzedColumn>(results, JSON.stringify)
const formattedSqlStatements = lodash.uniqBy(deduped, r => r.formatted_query)

assert.ok(
formattedSqlStatements.length <= 1,
`Expected exactly 1 formatted sql, got ${formattedSqlStatements.length}`,
)
await tx.query(sql`drop schema if exists ${schemaIdentifier} cascade`)

return deduped
})
// await client.query(sql`drop schema if exists ${schemaIdentifier} cascade`)
}

// can't use typegen here because it relies on a function in a temp schema
Expand Down Expand Up @@ -200,7 +201,10 @@ const createAnalyzeSelectStatementColumnsFunction = async (queryable: Queryable,
) t
) as column_aliases,
--'originally from table: ' || view_column_usage.table_name as comment,
null as comment,
col_description(
to_regclass(quote_ident(c.table_schema) || '.' || quote_ident(c.table_name)),
c.ordinal_position
) as comment,
view_column_usage.table_name as underlying_table_name,
c.is_nullable as is_underlying_nullable,
c.data_type as underlying_data_type,
Expand Down
1 change: 0 additions & 1 deletion packages/typegen/src/query/column-info.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,6 @@ const getFieldInfo = (

// determine nullability
let nullability: AnalysedQueryField['nullability'] = 'unknown'
console.log('checking nullability', {res})
if (res?.is_underlying_nullable === 'YES') {
nullability = 'nullable'
} else if (res?.hasNullableJoin) {
Expand Down
3 changes: 2 additions & 1 deletion packages/typegen/src/write/typescript.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ export const jsdocComment = (lines: Array<string | undefined | false>) => {
.split('\n')
.map(line => `* ${line}`)
.join('\n')
.replace(/pgkit_typegen_temp_schema_\w+\./, '')
// todo: avoid dumping this in the first place
.replace(/pgkit_typegen_temp_schema_\w+\./, '✨.')

return middle.includes('\n')
? `/**\n${middle}\n*/` // surround multiline comments with new lines
Expand Down
66 changes: 55 additions & 11 deletions packages/typegen/test/cte.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,10 +106,18 @@ test(`statement with CTE`, async () => {
/** - query: \`with abc as (select a as aaa from test_t... [truncated] ...b from abc join def on abc.aaa = def.bbb\` */
export interface Abc_Def {
/** column: \`abc.aaa\`, not null: \`true\`, regtype: \`integer\` */
/**
* From CTE expression "abc", column source: public.test_table1.a
*
* column: \`✨.abc.aaa\`, not null: \`true\`, regtype: \`integer\`
*/
aaa: number
/** column: \`def.bbb\`, regtype: \`double precision\` */
/**
* From CTE expression "def", column source: public.test_table2.b
*
* column: \`✨.def.bbb\`, regtype: \`double precision\`
*/
bbb: number | null
}
}
Expand Down Expand Up @@ -156,7 +164,11 @@ test(`statement with complex CTE`, async () => {
/** - query: \`with abc as (select table_name from info... [truncated] ...on_schema.tables, abc) select * from def\` */
export interface Def {
/** column: \`def.table_schema\`, regtype: \`name\` */
/**
* From CTE expression "def", column source: information_schema.tables.table_schema
*
* column: \`✨.def.table_schema\`, regtype: \`name\`
*/
table_schema: string | null
}
}
Expand Down Expand Up @@ -201,7 +213,11 @@ test(`statement with confusingly-named CTE`, async () => {
/** - query: \`with test_table1 as (select b as a from test_table2) select a from test_table1\` */
export interface TestTable1 {
/** column: \`test_table1.a\`, regtype: \`double precision\` */
/**
* From CTE expression "test_table1", column source: public.test_table2.b
*
* column: \`✨.test_table1.a\`, regtype: \`double precision\`
*/
a: number | null
}
}
Expand Down Expand Up @@ -270,25 +286,53 @@ test(`statement with CTE with crazy ordering`, async () => {
/** - query: \`with x as ( select b as b1, a as a1, a a... [truncated] ...ble1.a = test_table2.b ) select * from x\` */
export interface X {
/** column: \`x.b1\`, regtype: \`double precision\` */
/**
* From CTE expression "x", column source: public.test_table2.b
*
* column: \`✨.x.b1\`, regtype: \`double precision\`
*/
b1: number | null
/** column: \`x.a1\`, not null: \`true\`, regtype: \`integer\` */
/**
* From CTE expression "x", column source: public.test_table1.a
*
* column: \`✨.x.a1\`, not null: \`true\`, regtype: \`integer\`
*/
a1: number
/** column: \`x.a2\`, not null: \`true\`, regtype: \`integer\` */
/**
* From CTE expression "x", column source: public.test_table1.a
*
* column: \`✨.x.a2\`, not null: \`true\`, regtype: \`integer\`
*/
a2: number
/** column: \`x.a3\`, not null: \`true\`, regtype: \`integer\` */
/**
* From CTE expression "x", column source: public.test_table1.a
*
* column: \`✨.x.a3\`, not null: \`true\`, regtype: \`integer\`
*/
a3: number
/** column: \`x.b2\`, regtype: \`double precision\` */
/**
* From CTE expression "x", column source: public.test_table2.b
*
* column: \`✨.x.b2\`, regtype: \`double precision\`
*/
b2: number | null
/** column: \`x.a4\`, not null: \`true\`, regtype: \`integer\` */
/**
* From CTE expression "x", column source: public.test_table1.a
*
* column: \`✨.x.a4\`, not null: \`true\`, regtype: \`integer\`
*/
a4: number
/** column: \`x.b3\`, regtype: \`double precision\` */
/**
* From CTE expression "x", column source: public.test_table2.b
*
* column: \`✨.x.b3\`, regtype: \`double precision\`
*/
b3: number | null
}
}
Expand Down

0 comments on commit 5f987f6

Please sign in to comment.