Skip to content

Commit 35982c8

Browse files
committed
emit singular time units from toSql
1 parent 3730fa9 commit 35982c8

2 files changed

Lines changed: 42 additions & 10 deletions

File tree

src/parser/toSql.ts

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,10 @@ const RESERVED_KEYWORDS = new Set(
2323
.map((k) => k.toLowerCase()),
2424
)
2525

26+
function formatTimeUnit(value: number, unit: string): string {
27+
return `${value} ${value === 1 ? unit.replace(/S$/, "") : unit}`
28+
}
29+
2630
export function toSql(node: AST.Statement | AST.Statement[]): string {
2731
if (Array.isArray(node)) {
2832
return node.map((s) => statementToSql(s)).join(";\n")
@@ -588,16 +592,24 @@ function parquetConfigToSql(config: AST.ParquetConfig): string {
588592
function storagePolicyToSql(sp: AST.StoragePolicy): string {
589593
const clauses: string[] = []
590594
if (sp.toParquet) {
591-
clauses.push(`TO PARQUET ${sp.toParquet.value} ${sp.toParquet.unit}`)
595+
clauses.push(
596+
`TO PARQUET ${formatTimeUnit(sp.toParquet.value, sp.toParquet.unit)}`,
597+
)
592598
}
593599
if (sp.dropNative) {
594-
clauses.push(`DROP NATIVE ${sp.dropNative.value} ${sp.dropNative.unit}`)
600+
clauses.push(
601+
`DROP NATIVE ${formatTimeUnit(sp.dropNative.value, sp.dropNative.unit)}`,
602+
)
595603
}
596604
if (sp.dropLocal) {
597-
clauses.push(`DROP LOCAL ${sp.dropLocal.value} ${sp.dropLocal.unit}`)
605+
clauses.push(
606+
`DROP LOCAL ${formatTimeUnit(sp.dropLocal.value, sp.dropLocal.unit)}`,
607+
)
598608
}
599609
if (sp.dropRemote) {
600-
clauses.push(`DROP REMOTE ${sp.dropRemote.value} ${sp.dropRemote.unit}`)
610+
clauses.push(
611+
`DROP REMOTE ${formatTimeUnit(sp.dropRemote.value, sp.dropRemote.unit)}`,
612+
)
601613
}
602614
return `STORAGE POLICY(${clauses.join(", ")})`
603615
}
@@ -664,7 +676,7 @@ function createTableToSql(stmt: AST.CreateTableStatement): string {
664676
}
665677

666678
if (stmt.ttl) {
667-
parts.push(`TTL ${stmt.ttl.value} ${stmt.ttl.unit}`)
679+
parts.push(`TTL ${formatTimeUnit(stmt.ttl.value, stmt.ttl.unit)}`)
668680
}
669681

670682
if (stmt.storagePolicy) {
@@ -849,7 +861,7 @@ function alterTableToSql(stmt: AST.AlterTableStatement): string {
849861
break
850862
case "setTtl":
851863
parts.push("SET TTL")
852-
parts.push(`${action.ttl.value} ${action.ttl.unit}`)
864+
parts.push(formatTimeUnit(action.ttl.value, action.ttl.unit))
853865
break
854866
case "dedupDisable":
855867
parts.push("DEDUP DISABLE")
@@ -1111,7 +1123,8 @@ function createMaterializedViewToSql(
11111123
if (stmt.timestamp)
11121124
parts.push(`TIMESTAMP(${qualifiedNameToSql(stmt.timestamp)})`)
11131125
if (stmt.partitionBy) parts.push(`PARTITION BY ${stmt.partitionBy}`)
1114-
if (stmt.ttl) parts.push(`TTL ${stmt.ttl.value} ${stmt.ttl.unit}`)
1126+
if (stmt.ttl)
1127+
parts.push(`TTL ${formatTimeUnit(stmt.ttl.value, stmt.ttl.unit)}`)
11151128
if (stmt.storagePolicy) parts.push(storagePolicyToSql(stmt.storagePolicy))
11161129
if (stmt.volume) parts.push(`IN VOLUME ${escapeIdentifier(stmt.volume)}`)
11171130
if (stmt.ownedBy) parts.push(`OWNED BY ${escapeIdentifier(stmt.ownedBy)}`)
@@ -1138,10 +1151,12 @@ function alterMaterializedViewToSql(
11381151
)
11391152
break
11401153
case "setTtl":
1141-
parts.push(`SET TTL ${action.ttl.value} ${action.ttl.unit}`)
1154+
parts.push(`SET TTL ${formatTimeUnit(action.ttl.value, action.ttl.unit)}`)
11421155
break
11431156
case "setRefreshLimit":
1144-
parts.push(`SET REFRESH LIMIT ${action.limit.value} ${action.limit.unit}`)
1157+
parts.push(
1158+
`SET REFRESH LIMIT ${formatTimeUnit(action.limit.value, action.limit.unit)}`,
1159+
)
11451160
break
11461161
case "setRefresh": {
11471162
const refreshParts: string[] = ["SET"]

tests/parser.test.ts

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2279,10 +2279,27 @@ describe("QuestDB Parser", () => {
22792279
expect(result.errors).toHaveLength(0)
22802280
const sql = toSql(result.ast[0])
22812281
expect(sql).toContain(
2282-
"STORAGE POLICY(TO PARQUET 3 DAYS, DROP NATIVE 10 DAYS, DROP LOCAL 1 MONTHS)",
2282+
"STORAGE POLICY(TO PARQUET 3 DAYS, DROP NATIVE 10 DAYS, DROP LOCAL 1 MONTH)",
22832283
)
22842284
})
22852285

2286+
it("emits singular units for value 1, plural otherwise (matches SHOW CREATE)", () => {
2287+
const singular = toSql(
2288+
parseToAst(
2289+
"CREATE TABLE t (ts TIMESTAMP) TIMESTAMP(ts) PARTITION BY DAY TTL 1 MONTH",
2290+
).ast[0],
2291+
)
2292+
expect(singular).toContain("TTL 1 MONTH")
2293+
expect(singular).not.toContain("TTL 1 MONTHS")
2294+
2295+
const plural = toSql(
2296+
parseToAst(
2297+
"CREATE TABLE t (ts TIMESTAMP) TIMESTAMP(ts) PARTITION BY DAY TTL 3 MONTHS",
2298+
).ast[0],
2299+
)
2300+
expect(plural).toContain("TTL 3 MONTHS")
2301+
})
2302+
22862303
it("rejects an empty STORAGE POLICY() — at least one clause required", () => {
22872304
// QuestDB enforces this at parse time (EntSqlCompilerImpl):
22882305
// "at least one storage policy clause is required"

0 commit comments

Comments
 (0)