comparison src/cjr_print.sml @ 275:73456bfde988

Validating schema of a live database
author Adam Chlipala <adamc@hcoop.net>
date Tue, 02 Sep 2008 14:40:57 -0400
parents e4baf03a3a64
children ed4af33681d8
comparison
equal deleted inserted replaced
274:e4baf03a3a64 275:73456bfde988
690 end 690 end
691 | DTable (x, _) => box [string "/* SQL table ", 691 | DTable (x, _) => box [string "/* SQL table ",
692 string x, 692 string x,
693 string " */", 693 string " */",
694 newline] 694 newline]
695 | DDatabase s => box [string "void lw_db_init(lw_context ctx) {", 695 | DDatabase s => box [string "static void lw_db_validate(lw_context);",
696 newline, 696 newline,
697 string "PGresult *res;", 697 newline,
698 string "void lw_db_init(lw_context ctx) {",
698 newline, 699 newline,
699 string "PGconn *conn = PQconnectdb(\"", 700 string "PGconn *conn = PQconnectdb(\"",
700 string (String.toString s), 701 string (String.toString s),
701 string "\");", 702 string "\");",
702 newline, 703 newline,
718 newline, 719 newline,
719 string "}", 720 string "}",
720 newline, 721 newline,
721 string "lw_set_db(ctx, conn);", 722 string "lw_set_db(ctx, conn);",
722 newline, 723 newline,
724 string "lw_db_validate(ctx);",
725 newline,
723 string "}", 726 string "}",
724 newline, 727 newline,
725 newline, 728 newline,
726 string "void lw_db_close(lw_context ctx) {", 729 string "void lw_db_close(lw_context ctx) {",
727 newline, 730 newline,
733 datatype 'a search = 736 datatype 'a search =
734 Found of 'a 737 Found of 'a
735 | NotFound 738 | NotFound
736 | Error 739 | Error
737 740
741 fun p_sqltype' env (tAll as (t, loc)) =
742 case t of
743 TFfi ("Basis", "int") => "int8"
744 | TFfi ("Basis", "float") => "float8"
745 | TFfi ("Basis", "string") => "text"
746 | TFfi ("Basis", "bool") => "bool"
747 | _ => (ErrorMsg.errorAt loc "Don't know SQL equivalent of type";
748 Print.eprefaces' [("Type", p_typ env tAll)];
749 "ERROR")
750
751 fun p_sqltype env t = string (p_sqltype' env t)
738 752
739 fun p_file env (ds, ps) = 753 fun p_file env (ds, ps) =
740 let 754 let
741 val (pds, env) = ListUtil.foldlMap (fn (d, env) => 755 val (pds, env) = ListUtil.foldlMap (fn (d, env) =>
742 (p_decl env d, 756 (p_decl env d,
1202 string "}"] 1216 string "}"]
1203 ] 1217 ]
1204 end 1218 end
1205 1219
1206 val pds' = map p_page ps 1220 val pds' = map p_page ps
1221
1222 val tables = List.mapPartial (fn (DTable (s, xts), _) => SOME (s, xts)
1223 | _ => NONE) ds
1224
1225 val validate =
1226 box [string "static void lw_db_validate(lw_context ctx) {",
1227 newline,
1228 string "PGconn *conn = lw_get_db(ctx);",
1229 newline,
1230 string "PGresult *res;",
1231 newline,
1232 newline,
1233 p_list_sep newline
1234 (fn (s, xts) =>
1235 let
1236 val q = "SELECT COUNT(*) FROM pg_class WHERE relname = '"
1237 ^ s ^ "'"
1238
1239 val q' = String.concat ["SELECT COUNT(*) FROM pg_attribute WHERE attrelid = (SELECT oid FROM pg_class WHERE relname = '",
1240 s,
1241 "') AND (",
1242 String.concatWith " OR "
1243 (map (fn (x, t) =>
1244 String.concat ["(attname = 'lw_",
1245 CharVector.map
1246 Char.toLower x,
1247 "' AND atttypid = (SELECT oid FROM pg_type",
1248 " WHERE typname = '",
1249 p_sqltype' env t,
1250 "'))"]) xts),
1251 ")"]
1252
1253 val q'' = String.concat ["SELECT COUNT(*) FROM pg_attribute WHERE attrelid = (SELECT oid FROM pg_class WHERE relname = '",
1254 s,
1255 "') AND attnum >= 0"]
1256 in
1257 box [string "res = PQexec(conn, \"",
1258 string q,
1259 string "\");",
1260 newline,
1261 newline,
1262 string "if (res == NULL) {",
1263 newline,
1264 box [string "PQfinish(conn);",
1265 newline,
1266 string "lw_error(ctx, FATAL, \"Out of memory allocating query result.\");",
1267 newline],
1268 string "}",
1269 newline,
1270 newline,
1271 string "if (PQresultStatus(res) != PGRES_TUPLES_OK) {",
1272 newline,
1273 box [string "char msg[1024];",
1274 newline,
1275 string "strncpy(msg, PQerrorMessage(conn), 1024);",
1276 newline,
1277 string "msg[1023] = 0;",
1278 newline,
1279 string "PQclear(res);",
1280 newline,
1281 string "PQfinish(conn);",
1282 newline,
1283 string "lw_error(ctx, FATAL, \"Query failed:\\n",
1284 string q,
1285 string "\\n%s\", msg);",
1286 newline],
1287 string "}",
1288 newline,
1289 newline,
1290 string "if (strcmp(PQgetvalue(res, 0, 0), \"1\")) {",
1291 newline,
1292 box [string "PQclear(res);",
1293 newline,
1294 string "PQfinish(conn);",
1295 newline,
1296 string "lw_error(ctx, FATAL, \"Table '",
1297 string s,
1298 string "' does not exist.\");",
1299 newline],
1300 string "}",
1301 newline,
1302 newline,
1303 string "PQclear(res);",
1304 newline,
1305
1306 string "res = PQexec(conn, \"",
1307 string q',
1308 string "\");",
1309 newline,
1310 newline,
1311 string "if (res == NULL) {",
1312 newline,
1313 box [string "PQfinish(conn);",
1314 newline,
1315 string "lw_error(ctx, FATAL, \"Out of memory allocating query result.\");",
1316 newline],
1317 string "}",
1318 newline,
1319 newline,
1320 string "if (PQresultStatus(res) != PGRES_TUPLES_OK) {",
1321 newline,
1322 box [string "char msg[1024];",
1323 newline,
1324 string "strncpy(msg, PQerrorMessage(conn), 1024);",
1325 newline,
1326 string "msg[1023] = 0;",
1327 newline,
1328 string "PQclear(res);",
1329 newline,
1330 string "PQfinish(conn);",
1331 newline,
1332 string "lw_error(ctx, FATAL, \"Query failed:\\n",
1333 string q',
1334 string "\\n%s\", msg);",
1335 newline],
1336 string "}",
1337 newline,
1338 newline,
1339 string "if (strcmp(PQgetvalue(res, 0, 0), \"",
1340 string (Int.toString (length xts)),
1341 string "\")) {",
1342 newline,
1343 box [string "PQclear(res);",
1344 newline,
1345 string "PQfinish(conn);",
1346 newline,
1347 string "lw_error(ctx, FATAL, \"Table '",
1348 string s,
1349 string "' has the wrong column types.\");",
1350 newline],
1351 string "}",
1352 newline,
1353 newline,
1354 string "PQclear(res);",
1355 newline,
1356 newline,
1357
1358 string "res = PQexec(conn, \"",
1359 string q'',
1360 string "\");",
1361 newline,
1362 newline,
1363 string "if (res == NULL) {",
1364 newline,
1365 box [string "PQfinish(conn);",
1366 newline,
1367 string "lw_error(ctx, FATAL, \"Out of memory allocating query result.\");",
1368 newline],
1369 string "}",
1370 newline,
1371 newline,
1372 string "if (PQresultStatus(res) != PGRES_TUPLES_OK) {",
1373 newline,
1374 box [string "char msg[1024];",
1375 newline,
1376 string "strncpy(msg, PQerrorMessage(conn), 1024);",
1377 newline,
1378 string "msg[1023] = 0;",
1379 newline,
1380 string "PQclear(res);",
1381 newline,
1382 string "PQfinish(conn);",
1383 newline,
1384 string "lw_error(ctx, FATAL, \"Query failed:\\n",
1385 string q'',
1386 string "\\n%s\", msg);",
1387 newline],
1388 string "}",
1389 newline,
1390 newline,
1391 string "if (strcmp(PQgetvalue(res, 0, 0), \"",
1392 string (Int.toString (length xts)),
1393 string "\")) {",
1394 newline,
1395 box [string "PQclear(res);",
1396 newline,
1397 string "PQfinish(conn);",
1398 newline,
1399 string "lw_error(ctx, FATAL, \"Table '",
1400 string s,
1401 string "' has extra columns.\");",
1402 newline],
1403 string "}",
1404 newline,
1405 newline,
1406 string "PQclear(res);",
1407 newline]
1408 end) tables,
1409 string "}"]
1207 in 1410 in
1208 box [string "#include <stdio.h>", 1411 box [string "#include <stdio.h>",
1209 newline, 1412 newline,
1210 string "#include <stdlib.h>", 1413 string "#include <stdlib.h>",
1211 newline, 1414 newline,
1233 string "void lw_handle(lw_context ctx, char *request) {", 1436 string "void lw_handle(lw_context ctx, char *request) {",
1234 newline, 1437 newline,
1235 p_list_sep newline (fn x => x) pds', 1438 p_list_sep newline (fn x => x) pds',
1236 newline, 1439 newline,
1237 string "}", 1440 string "}",
1441 newline,
1442 newline,
1443 validate,
1238 newline] 1444 newline]
1239 end
1240
1241 fun p_sqltype env (tAll as (t, loc)) =
1242 let
1243 val s = case t of
1244 TFfi ("Basis", "int") => "int8"
1245 | TFfi ("Basis", "float") => "float8"
1246 | TFfi ("Basis", "string") => "text"
1247 | TFfi ("Basis", "bool") => "bool"
1248 | _ => (ErrorMsg.errorAt loc "Don't know SQL equivalent of type";
1249 Print.eprefaces' [("Type", p_typ env tAll)];
1250 "ERROR")
1251 in
1252 string s
1253 end 1445 end
1254 1446
1255 fun p_sql env (ds, _) = 1447 fun p_sql env (ds, _) =
1256 let 1448 let
1257 val (pps, _) = ListUtil.foldlMap 1449 val (pps, _) = ListUtil.foldlMap
1262 box [string "CREATE TABLE ", 1454 box [string "CREATE TABLE ",
1263 string s, 1455 string s,
1264 string "(", 1456 string "(",
1265 p_list (fn (x, t) => 1457 p_list (fn (x, t) =>
1266 box [string "lw_", 1458 box [string "lw_",
1267 string x, 1459 string (CharVector.map Char.toLower x),
1268 space,
1269 string ":",
1270 space, 1460 space,
1271 p_sqltype env t, 1461 p_sqltype env t,
1272 space, 1462 space,
1273 string "NOT", 1463 string "NOT",
1274 space, 1464 space,