Skip to content

Commit 6c24385

Browse files
committed
fix(typegen): add better order stability for functions override
1 parent 5bdb9c7 commit 6c24385

File tree

2 files changed

+123
-2
lines changed

2 files changed

+123
-2
lines changed

src/server/templates/typescript.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -681,8 +681,10 @@ export type Database = {
681681
{} as Record<string, typeof schemaFunctions>
682682
)
683683
for (const fnName in schemaFunctionsGroupedByName) {
684-
schemaFunctionsGroupedByName[fnName].sort((a, b) =>
685-
b.fn.definition.localeCompare(a.fn.definition)
684+
schemaFunctionsGroupedByName[fnName].sort(
685+
(a, b) =>
686+
a.fn.argument_types.localeCompare(b.fn.argument_types) ||
687+
a.fn.return_type.localeCompare(b.fn.return_type)
686688
)
687689
}
688690

test/server/typegen.ts

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4992,6 +4992,125 @@ test('typegen: typescript consistent types definitions orders', async () => {
49924992
expect(firstCall).toEqual(secondCall)
49934993
})
49944994

4995+
test('typegen: typescript function override order stability', async () => {
4996+
// Helper function to clean up test entities
4997+
const cleanupTestEntities = async () => {
4998+
await app.inject({
4999+
method: 'POST',
5000+
path: '/query',
5001+
payload: {
5002+
query: `
5003+
-- Drop functions with all possible signatures
5004+
DROP FUNCTION IF EXISTS test_func_override(integer, text) CASCADE;
5005+
DROP FUNCTION IF EXISTS test_func_override(text, integer) CASCADE;
5006+
DROP FUNCTION IF EXISTS test_func_override(boolean, integer, text) CASCADE;
5007+
DROP FUNCTION IF EXISTS test_func_override(text, boolean) CASCADE;
5008+
`,
5009+
},
5010+
})
5011+
}
5012+
5013+
// Clean up any existing test entities
5014+
await cleanupTestEntities()
5015+
5016+
// === FIRST ROUND: Create function overrides in order 1 ===
5017+
await app.inject({
5018+
method: 'POST',
5019+
path: '/query',
5020+
payload: {
5021+
query: `
5022+
-- Create function overrides in specific order
5023+
CREATE FUNCTION test_func_override(param_a integer, param_b text)
5024+
RETURNS integer AS 'SELECT param_a + 1' LANGUAGE sql IMMUTABLE;
5025+
5026+
CREATE FUNCTION test_func_override(param_a text, param_b integer)
5027+
RETURNS text AS 'SELECT param_a || param_b::text' LANGUAGE sql IMMUTABLE;
5028+
5029+
CREATE FUNCTION test_func_override(param_a boolean, param_b integer, param_c text)
5030+
RETURNS boolean AS 'SELECT param_a' LANGUAGE sql IMMUTABLE;
5031+
5032+
CREATE FUNCTION test_func_override(param_a text, param_b boolean)
5033+
RETURNS text AS 'SELECT CASE WHEN param_b THEN param_a ELSE '''' END' LANGUAGE sql IMMUTABLE;
5034+
`,
5035+
},
5036+
})
5037+
5038+
// Generate types for first configuration
5039+
const { body: firstCall } = await app.inject({
5040+
method: 'GET',
5041+
path: '/generators/typescript',
5042+
query: { detect_one_to_one_relationships: 'true', postgrest_version: '13' },
5043+
})
5044+
5045+
// === SECOND ROUND: Modify function definitions without changing signatures ===
5046+
await app.inject({
5047+
method: 'POST',
5048+
path: '/query',
5049+
payload: {
5050+
query: `
5051+
-- Modify function definitions (using CREATE OR REPLACE)
5052+
-- This should preserve the order
5053+
CREATE OR REPLACE FUNCTION test_func_override(param_a integer, param_b text)
5054+
RETURNS integer AS 'SELECT param_a + 100' LANGUAGE sql IMMUTABLE;
5055+
5056+
CREATE OR REPLACE FUNCTION test_func_override(param_a text, param_b integer)
5057+
RETURNS text AS 'SELECT param_a || ''_'' || param_b::text' LANGUAGE sql IMMUTABLE;
5058+
5059+
CREATE OR REPLACE FUNCTION test_func_override(param_a boolean, param_b integer, param_c text)
5060+
RETURNS boolean AS 'SELECT NOT param_a' LANGUAGE sql IMMUTABLE;
5061+
5062+
CREATE OR REPLACE FUNCTION test_func_override(param_a text, param_b boolean)
5063+
RETURNS text AS 'SELECT CASE WHEN param_b THEN param_a || ''_true'' ELSE ''false'' END' LANGUAGE sql IMMUTABLE;
5064+
`,
5065+
},
5066+
})
5067+
5068+
// Generate types for second configuration (after modifying definitions)
5069+
const { body: secondCall } = await app.inject({
5070+
method: 'GET',
5071+
path: '/generators/typescript',
5072+
query: { detect_one_to_one_relationships: 'true', postgrest_version: '13' },
5073+
})
5074+
5075+
// === THIRD ROUND: Drop and recreate in different order ===
5076+
await cleanupTestEntities()
5077+
5078+
// Create functions in reverse order
5079+
await app.inject({
5080+
method: 'POST',
5081+
path: '/query',
5082+
payload: {
5083+
query: `
5084+
-- Create function overrides in reverse order
5085+
CREATE FUNCTION test_func_override(param_a text, param_b boolean)
5086+
RETURNS text AS 'SELECT CASE WHEN param_b THEN param_a ELSE '''' END' LANGUAGE sql IMMUTABLE;
5087+
5088+
CREATE FUNCTION test_func_override(param_a boolean, param_b integer, param_c text)
5089+
RETURNS boolean AS 'SELECT param_a' LANGUAGE sql IMMUTABLE;
5090+
5091+
CREATE FUNCTION test_func_override(param_a text, param_b integer)
5092+
RETURNS text AS 'SELECT param_a || param_b::text' LANGUAGE sql IMMUTABLE;
5093+
5094+
CREATE FUNCTION test_func_override(param_a integer, param_b text)
5095+
RETURNS integer AS 'SELECT param_a + 1' LANGUAGE sql IMMUTABLE;
5096+
`,
5097+
},
5098+
})
5099+
5100+
// Generate types for third configuration (recreated in different order)
5101+
const { body: thirdCall } = await app.inject({
5102+
method: 'GET',
5103+
path: '/generators/typescript',
5104+
query: { detect_one_to_one_relationships: 'true', postgrest_version: '13' },
5105+
})
5106+
5107+
// Clean up test entities
5108+
await cleanupTestEntities()
5109+
5110+
expect(firstCall).toEqual(secondCall)
5111+
expect(secondCall).toEqual(thirdCall)
5112+
})
5113+
49955114
test('typegen: go', async () => {
49965115
const { body } = await app.inject({ method: 'GET', path: '/generators/go' })
49975116
expect(body).toMatchInlineSnapshot(`

0 commit comments

Comments
 (0)