comparison src/postgres.sml @ 2177:00cf8214c2e3

Switching to a more dynamic method of handling database reconnection, restarting transactions
author Adam Chlipala <adam@chlipala.net>
date Sat, 17 Oct 2015 11:08:12 -0400
parents d2a98983f502
children 251dd276f45f
comparison
equal deleted inserted replaced
2176:d2a98983f502 2177:00cf8214c2e3
1 (* Copyright (c) 2008-2010, 2015, Adam Chlipala 1 (* Copyright (c) 2008-2010, Adam Chlipala
2 * All rights reserved. 2 * All rights reserved.
3 * 3 *
4 * Redistribution and use in source and binary forms, with or without 4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are met: 5 * modification, are permitted provided that the following conditions are met:
6 * 6 *
518 newline, 518 newline,
519 string "msg[1023] = 0;", 519 string "msg[1023] = 0;",
520 newline, 520 newline,
521 string "PQfinish(conn);", 521 string "PQfinish(conn);",
522 newline, 522 newline,
523 string "uw_error(ctx, FATAL, ", 523 string "uw_error(ctx, BOUNDED_RETRY, ",
524 string "\"Connection to Postgres server failed: %s\", msg);"], 524 string "\"Connection to Postgres server failed: %s\", msg);"],
525 newline, 525 newline,
526 string "}", 526 string "}",
527 newline, 527 newline,
528 string "uw_set_db(ctx, conn);", 528 string "uw_set_db(ctx, conn);",
610 string ")"] 610 string ")"]
611 in 611 in
612 getter t 612 getter t
613 end 613 end
614 614
615 fun queryCommon {loc, query, cols, doCols, runit} = 615 fun queryCommon {loc, query, cols, doCols} =
616 box [string "int n, i;", 616 box [string "int n, i;",
617 newline, 617 newline,
618 newline, 618 newline,
619 619
620 string "if (res == NULL) {", 620 string "if (res == NULL) {",
621 box [newline, 621 box [newline,
622 string "if (uw_try_reconnecting_if_at_most_one(ctx)) {", 622 string "uw_try_reconnecting_and_restarting(ctx);",
623 box [newline, 623 newline,
624 string "conn = uw_get_db(ctx);", 624 string "uw_error(ctx, FATAL, \"Can't allocate query result; database server may be down.\");",
625 newline,
626 runit,
627 newline],
628 string "}",
629 newline,
630 string "if (res == NULL) uw_error(ctx, FATAL, \"Can't allocate query result; database server might be down.\");",
631 newline], 625 newline],
632 string "}", 626 string "}",
633 newline, 627 newline,
634 newline, 628 newline,
635 629
697 newline, 691 newline,
698 string "uw_pop_cleanup(ctx);", 692 string "uw_pop_cleanup(ctx);",
699 newline] 693 newline]
700 694
701 fun query {loc, cols, doCols} = 695 fun query {loc, cols, doCols} =
702 let 696 box [string "PGconn *conn = uw_get_db(ctx);",
703 val runit = string "res = PQexecParams(conn, query, 0, NULL, NULL, NULL, NULL, 0);" 697 newline,
704 in 698 string "PGresult *res = PQexecParams(conn, query, 0, NULL, NULL, NULL, NULL, 0);",
705 box [string "PGconn *conn = uw_get_db(ctx);", 699 newline,
706 newline, 700 newline,
707 string "PGresult *res;", 701 queryCommon {loc = loc, cols = cols, doCols = doCols, query = string "query"}]
708 newline,
709 runit,
710 newline,
711 newline,
712 queryCommon {loc = loc, cols = cols, doCols = doCols, query = string "query", runit = runit}]
713 end
714 702
715 fun p_ensql t e = 703 fun p_ensql t e =
716 case t of 704 case t of
717 Int => box [string "uw_Basis_attrifyInt(ctx, ", e, string ")"] 705 Int => box [string "uw_Basis_attrifyInt(ctx, ", e, string ")"]
718 | Float => box [string "uw_Basis_attrifyFloat(ctx, ", e, string ")"] 706 | Float => box [string "uw_Basis_attrifyFloat(ctx, ", e, string ")"]
772 newline]) 760 newline])
773 inputs, 761 inputs,
774 newline] 762 newline]
775 763
776 fun queryPrepared {loc, id, query, inputs, cols, doCols, nested = _} = 764 fun queryPrepared {loc, id, query, inputs, cols, doCols, nested = _} =
777 let 765 box [string "PGconn *conn = uw_get_db(ctx);",
778 val runit = 766 newline,
779 box [string "res = ", 767
780 if #persistent (Settings.currentProtocol ()) then 768 makeParams inputs,
781 box [string "PQexecPrepared(conn, \"uw", 769
782 string (Int.toString id), 770 newline,
783 string "\", ", 771 string "PGresult *res = ",
784 string (Int.toString (length inputs)), 772 if #persistent (Settings.currentProtocol ()) then
785 string ", paramValues, paramLengths, paramFormats, 0);"] 773 box [string "PQexecPrepared(conn, \"uw",
786 else 774 string (Int.toString id),
787 box [string "PQexecParams(conn, \"", 775 string "\", ",
788 string (Prim.toCString query), 776 string (Int.toString (length inputs)),
789 string "\", ", 777 string ", paramValues, paramLengths, paramFormats, 0);"]
790 string (Int.toString (length inputs)), 778 else
791 string ", NULL, paramValues, paramLengths, paramFormats, 0);"]] 779 box [string "PQexecParams(conn, \"",
792 in 780 string (Prim.toCString query),
793 box [string "PGconn *conn = uw_get_db(ctx);", 781 string "\", ",
794 newline, 782 string (Int.toString (length inputs)),
795 783 string ", NULL, paramValues, paramLengths, paramFormats, 0);"],
796 makeParams inputs, 784 newline,
797 785 newline,
798 newline, 786 queryCommon {loc = loc, cols = cols, doCols = doCols, query = box [string "\"",
799 string "PGresult *res;", 787 string (Prim.toCString query),
800 runit, 788 string "\""]}]
801 newline, 789
802 newline, 790 fun dmlCommon {loc, dml, mode} =
803 queryCommon {loc = loc, cols = cols, doCols = doCols, query = box [string "\"",
804 string (Prim.toCString query),
805 string "\""],
806 runit = runit}]
807 end
808
809 fun dmlCommon {loc, dml, mode, runit} =
810 box [string "if (res == NULL) {", 791 box [string "if (res == NULL) {",
811 box [newline, 792 box [newline,
812 string "if (uw_try_reconnecting_if_at_most_one(ctx)) {", 793 string "uw_try_reconnecting_and_restarting(ctx);",
813 box [newline, 794 newline,
814 string "conn = uw_get_db(ctx);", 795 string "uw_error(ctx, FATAL, \"Can't allocate DML result; database server may be down.\");",
815 newline,
816 runit,
817 newline],
818 string "}",
819 newline,
820 string "if (res == NULL) uw_error(ctx, FATAL, \"Can't allocate DML result; database server might be down.\");",
821 newline], 796 newline],
822 string "}", 797 string "}",
823 newline, 798 newline,
824 newline, 799 newline,
825 800
855 830
856 string "res = PQexec(conn, \"ROLLBACK TO s\");", 831 string "res = PQexec(conn, \"ROLLBACK TO s\");",
857 newline, 832 newline,
858 string "if (res == NULL) {", 833 string "if (res == NULL) {",
859 box [newline, 834 box [newline,
860 string "uw_error(ctx, FATAL, \"Can't allocate DML ROLLBACK result; database server might be down.\");", 835 string "uw_try_reconnecting_and_restarting(ctx);",
836 newline,
837 string "uw_error(ctx, FATAL, \"Can't allocate DML ROLLBACK result; database server may be down.\");",
861 newline], 838 newline],
862 string "}", 839 string "}",
863 newline, 840 newline,
864 newline, 841 newline,
865 842
890 newline, 867 newline,
891 box [string "PQclear(res);", 868 box [string "PQclear(res);",
892 newline, 869 newline,
893 string "res = PQexec(conn, \"RELEASE s\");", 870 string "res = PQexec(conn, \"RELEASE s\");",
894 newline, 871 newline,
895 string "if (res == NULL) uw_error(ctx, FATAL, \"Out of memory allocating DML RELEASE result.\");", 872 string "if (res == NULL) {",
873 box [newline,
874 string "uw_try_reconnecting_and_restarting(ctx);",
875 newline,
876 string "uw_error(ctx, FATAL, \"Can't allocate DML RELEASE result; database server may be down.\");",
877 newline],
878 string "}",
896 newline, 879 newline,
897 newline, 880 newline,
898 881
899 string "if (PQresultStatus(res) != PGRES_COMMAND_OK) {", 882 string "if (PQresultStatus(res) != PGRES_COMMAND_OK) {",
900 newline, 883 newline,
916 fun makeSavepoint mode = 899 fun makeSavepoint mode =
917 case mode of 900 case mode of
918 Error => box [] 901 Error => box []
919 | None => box [string "res = PQexec(conn, \"SAVEPOINT s\");", 902 | None => box [string "res = PQexec(conn, \"SAVEPOINT s\");",
920 newline, 903 newline,
921 string "if (res == NULL) uw_error(ctx, FATAL, \"Out of memory allocating DML SAVEPOINT result.\");", 904 string "if (res == NULL) {",
905 box [newline,
906 string "uw_try_reconnecting_and_restarting(ctx);",
907 newline,
908 string "uw_error(ctx, FATAL, \"Can't allocate DML SAVEPOINT result; database server may be down.\");",
909 newline],
910 string "}",
922 newline, 911 newline,
923 newline, 912 newline,
924 string "if (PQresultStatus(res) != PGRES_COMMAND_OK) {", 913 string "if (PQresultStatus(res) != PGRES_COMMAND_OK) {",
925 box [newline, 914 box [newline,
926 string "PQclear(res);", 915 string "PQclear(res);",
932 string "PQclear(res);", 921 string "PQclear(res);",
933 newline, 922 newline,
934 newline] 923 newline]
935 924
936 fun dml (loc, mode) = 925 fun dml (loc, mode) =
937 let 926 box [string "PGconn *conn = uw_get_db(ctx);",
938 val runit = string "res = PQexecParams(conn, dml, 0, NULL, NULL, NULL, NULL, 0);" 927 newline,
939 in 928 string "PGresult *res;",
940 box [string "PGconn *conn = uw_get_db(ctx);", 929 newline,
941 newline, 930
942 string "PGresult *res;", 931 makeSavepoint mode,
943 newline, 932
944 933 string "res = PQexecParams(conn, dml, 0, NULL, NULL, NULL, NULL, 0);",
945 makeSavepoint mode, 934 newline,
946 935 newline,
947 runit, 936 dmlCommon {loc = loc, dml = string "dml", mode = mode}]
948 newline,
949 newline,
950 dmlCommon {loc = loc, dml = string "dml", mode = mode, runit = runit}]
951 end
952 937
953 fun dmlPrepared {loc, id, dml, inputs, mode} = 938 fun dmlPrepared {loc, id, dml, inputs, mode} =
954 let 939 box [string "PGconn *conn = uw_get_db(ctx);",
955 val runit = 940 newline,
956 box [string "res = ", 941
957 if #persistent (Settings.currentProtocol ()) then 942 makeParams inputs,
958 box [string "PQexecPrepared(conn, \"uw", 943
959 string (Int.toString id), 944 newline,
960 string "\", ", 945 string "PGresult *res;",
961 string (Int.toString (length inputs)), 946 newline,
962 string ", paramValues, paramLengths, paramFormats, 0);"] 947 newline,
963 else 948
964 box [string "PQexecParams(conn, \"", 949 makeSavepoint mode,
965 string (Prim.toCString dml), 950
966 string "\", ", 951 string "res = ",
967 string (Int.toString (length inputs)), 952 if #persistent (Settings.currentProtocol ()) then
968 string ", NULL, paramValues, paramLengths, paramFormats, 0);"]] 953 box [string "PQexecPrepared(conn, \"uw",
969 in 954 string (Int.toString id),
970 box [string "PGconn *conn = uw_get_db(ctx);", 955 string "\", ",
971 newline, 956 string (Int.toString (length inputs)),
972 957 string ", paramValues, paramLengths, paramFormats, 0);"]
973 makeParams inputs, 958 else
974 959 box [string "PQexecParams(conn, \"",
975 newline, 960 string (Prim.toCString dml),
976 string "PGresult *res;", 961 string "\", ",
977 newline, 962 string (Int.toString (length inputs)),
978 newline, 963 string ", NULL, paramValues, paramLengths, paramFormats, 0);"],
979 964 newline,
980 makeSavepoint mode, 965 newline,
981 966 dmlCommon {loc = loc, dml = box [string "\"",
982 runit, 967 string (Prim.toCString dml),
983 newline, 968 string "\""], mode = mode}]
984 newline, 969
985 dmlCommon {loc = loc, dml = box [string "\"", 970 fun nextvalCommon {loc, query} =
986 string (Prim.toCString dml),
987 string "\""], mode = mode, runit = runit}]
988 end
989
990 fun nextvalCommon {loc, query, runit} =
991 box [string "if (res == NULL) {", 971 box [string "if (res == NULL) {",
992 box [newline, 972 box [newline,
993 string "if (uw_try_reconnecting_if_at_most_one(ctx))", 973 string "uw_try_reconnecting_and_restarting(ctx);",
994 newline, 974 newline,
995 string "conn = uw_get_db(ctx);", 975 string "uw_error(ctx, FATAL, \"Can't allocate NEXTVAL result; database server may be down.\");",
996 newline,
997 runit,
998 newline,
999 string "uw_error(ctx, FATAL, \"Out of memory allocating nextval result.\");",
1000 newline], 976 newline],
1001 string "}", 977 string "}",
1002 newline, 978 newline,
1003 newline, 979 newline,
1004 980
1045 SOME s => 1021 SOME s =>
1046 string ("\"SELECT NEXTVAL('" ^ s ^ "')\"") 1022 string ("\"SELECT NEXTVAL('" ^ s ^ "')\"")
1047 | _ => box [string "uw_Basis_strcat(ctx, \"SELECT NEXTVAL('\", uw_Basis_strcat(ctx, ", 1023 | _ => box [string "uw_Basis_strcat(ctx, \"SELECT NEXTVAL('\", uw_Basis_strcat(ctx, ",
1048 seqE, 1024 seqE,
1049 string ", \"')\"))"] 1025 string ", \"')\"))"]
1050
1051 val runit = string "res = PQexecParams(conn, query, 0, NULL, NULL, NULL, NULL, 0);"
1052 in 1026 in
1053 box [string "char *query = ", 1027 box [string "char *query = ",
1054 query, 1028 query,
1055 string ";", 1029 string ";",
1056 newline, 1030 newline,
1057 string "PGconn *conn = uw_get_db(ctx);", 1031 string "PGconn *conn = uw_get_db(ctx);",
1058 newline, 1032 newline,
1059 string "PGresult *res;", 1033 string "PGresult *res = PQexecParams(conn, query, 0, NULL, NULL, NULL, NULL, 0);",
1060 newline, 1034 newline,
1061 runit, 1035 newline,
1062 newline, 1036 nextvalCommon {loc = loc, query = string "query"}]
1063 newline,
1064 nextvalCommon {loc = loc, query = string "query", runit = runit}]
1065 end 1037 end
1066 1038
1067 fun nextvalPrepared {loc, id, query} = 1039 fun nextvalPrepared {loc, id, query} =
1068 let 1040 box [string "PGconn *conn = uw_get_db(ctx);",
1069 val runit = 1041 newline,
1070 box [string "res = ", 1042 newline,
1071 if #persistent (Settings.currentProtocol ()) then 1043 string "PGresult *res = ",
1072 box [string "PQexecPrepared(conn, \"uw", 1044 if #persistent (Settings.currentProtocol ()) then
1073 string (Int.toString id), 1045 box [string "PQexecPrepared(conn, \"uw",
1074 string "\", 0, NULL, NULL, NULL, 0);"] 1046 string (Int.toString id),
1075 else 1047 string "\", 0, NULL, NULL, NULL, 0);"]
1076 box [string "PQexecParams(conn, \"", 1048 else
1077 string (Prim.toCString query), 1049 box [string "PQexecParams(conn, \"",
1078 string "\", 0, NULL, NULL, NULL, NULL, 0);"]] 1050 string (Prim.toCString query),
1079 in 1051 string "\", 0, NULL, NULL, NULL, NULL, 0);"],
1080 box [string "PGconn *conn = uw_get_db(ctx);", 1052 newline,
1081 newline, 1053 newline,
1082 newline, 1054 nextvalCommon {loc = loc, query = box [string "\"",
1083 1055 string (Prim.toCString query),
1084 string "PGresult *res;", 1056 string "\""]}]
1085 newline, 1057
1086 runit, 1058 fun setvalCommon {loc, query} =
1087 newline,
1088 newline,
1089 nextvalCommon {loc = loc, query = box [string "\"",
1090 string (Prim.toCString query),
1091 string "\""], runit = runit}]
1092 end
1093
1094 fun setvalCommon {loc, query, runit} =
1095 box [string "if (res == NULL) {", 1059 box [string "if (res == NULL) {",
1096 box [newline, 1060 box [newline,
1097 string "if (uw_try_reconnecting_if_at_most_one(ctx))", 1061 string "uw_try_reconnecting_and_restarting(ctx);",
1098 newline, 1062 newline,
1099 string "conn = uw_get_db(ctx);", 1063 string "uw_error(ctx, FATAL, \"Can't allocate SETVAL result; database server may be down.\");",
1100 newline,
1101 runit,
1102 newline,
1103 string "uw_error(ctx, FATAL, \"Out of memory allocating setval result.\");",
1104 newline], 1064 newline],
1105 string "}", 1065 string "}",
1106 newline, 1066 newline,
1107 newline, 1067 newline,
1108 1068
1128 val query = box [string "uw_Basis_strcat(ctx, \"SELECT SETVAL('\", uw_Basis_strcat(ctx, ", 1088 val query = box [string "uw_Basis_strcat(ctx, \"SELECT SETVAL('\", uw_Basis_strcat(ctx, ",
1129 seqE, 1089 seqE,
1130 string ", uw_Basis_strcat(ctx, \"', \", uw_Basis_strcat(ctx, uw_Basis_sqlifyInt(ctx, ", 1090 string ", uw_Basis_strcat(ctx, \"', \", uw_Basis_strcat(ctx, uw_Basis_sqlifyInt(ctx, ",
1131 count, 1091 count,
1132 string "), \")\"))))"] 1092 string "), \")\"))))"]
1133
1134 val runit = string "res = PQexecParams(conn, query, 0, NULL, NULL, NULL, NULL, 0);"
1135 in 1093 in
1136 box [string "char *query = ", 1094 box [string "char *query = ",
1137 query, 1095 query,
1138 string ";", 1096 string ";",
1139 newline, 1097 newline,
1140 string "PGconn *conn = uw_get_db(ctx);", 1098 string "PGconn *conn = uw_get_db(ctx);",
1141 newline, 1099 newline,
1142 1100 string "PGresult *res = PQexecParams(conn, query, 0, NULL, NULL, NULL, NULL, 0);",
1143 string "PGresult *res;", 1101 newline,
1144 newline, 1102 newline,
1145 runit, 1103 setvalCommon {loc = loc, query = string "query"}]
1146 newline,
1147 newline,
1148 setvalCommon {loc = loc, query = string "query", runit = runit}]
1149 end 1104 end
1150 1105
1151 fun sqlifyString s = "E'" ^ String.translate (fn #"'" => "\\'" 1106 fun sqlifyString s = "E'" ^ String.translate (fn #"'" => "\\'"
1152 | #"\\" => "\\\\" 1107 | #"\\" => "\\\\"
1153 | ch => 1108 | ch =>