feat: add ID generators, fix 6 forum-discovered deficiencies

ID Generators:
- AUTO_INCREMENT for INTEGER columns
- SERIAL / BIGSERIAL as syntactic sugar
- gen_random_uuid() / uuid() for UUID v4 generation
- nextval() / currval() for sequence support
- snowflake_id(node_id) for distributed 64-bit IDs
- RETURNING clause for INSERT

Deficiency Fixes:
- Comma join: FROM t1, t2 now works as implicit CROSS JOIN
- DEFAULT in restoreSchema: defaults survive server restart
- Duplicate column names: t.id instead of id in joins
- Empty result sets: always send column metadata to client
- GROUP BY non-agg columns: return first row value instead of empty
- Aggregate column names: count(*) instead of count()

Bug fix: PK uniqueness check now uses correct key format
This commit is contained in:
2026-05-16 02:57:30 +03:00
parent 406dcf4f4e
commit 652ed1b477
7 changed files with 591 additions and 21 deletions
+29 -2
View File
@@ -526,7 +526,23 @@ proc parseSelect(p: var Parser): Node =
elif p.peek().kind == tkIdent:
alias = p.advance().value
result.selFrom = Node(kind: nkFrom, fromTable: tableTok.value,
fromAlias: alias, line: tableTok.line, col: tableTok.col)
fromAlias: alias, line: tableTok.line, col: tableTok.col)
# Comma join: FROM t1, t2 → implicit CROSS JOIN
while p.peek().kind == tkComma:
discard p.advance()
let nextTableTok = p.expect(tkIdent)
var nextAlias = ""
if p.match(tkAs):
nextAlias = p.expect(tkIdent).value
elif p.peek().kind == tkIdent:
nextAlias = p.advance().value
let joinNode = Node(kind: nkJoin, joinKind: jkCross,
joinTarget: Node(kind: nkFrom, fromTable: nextTableTok.value,
fromAlias: nextAlias, line: nextTableTok.line, col: nextTableTok.col),
joinOn: nil, joinAlias: nextAlias, joinLateral: false,
line: nextTableTok.line, col: nextTableTok.col)
result.selJoins.add(joinNode)
# Parse PIVOT / UNPIVOT after FROM source
if p.peek().kind == tkPivot:
@@ -1006,13 +1022,24 @@ proc parseCreateTable(p: var Parser): Node =
let colDef = Node(kind: nkColumnDef, cdName: colName, cdType: colType)
colDef.cdConstraints = @[]
# SERIAL / BIGSERIAL imply AUTO_INCREMENT
if colType in @["SERIAL", "BIGSERIAL"]:
colDef.cdAutoIncrement = true
if colType == "SERIAL":
colDef.cdType = "INTEGER"
else:
colDef.cdType = "BIGINT"
# Parse column constraints
while p.peek().kind in {tkPrimary, tkNot, tkNull, tkUnique, tkCheck, tkDefault, tkReferences}:
while p.peek().kind in {tkPrimary, tkNot, tkNull, tkUnique, tkCheck, tkDefault, tkReferences, tkAutoIncrement}:
let cst = Node(kind: nkConstraintDef)
cst.cstColumns = @[colName]; cst.cstRefColumns = @[]
if p.match(tkPrimary):
discard p.expect(tkKey)
cst.cstType = "pkey"
elif p.match(tkAutoIncrement):
colDef.cdAutoIncrement = true
continue
elif p.match(tkNot):
discard p.expect(tkNull)
cst.cstType = "notnull"