Mercurial > urweb
comparison src/mysql.sml @ 875:c50101ddf7fa
demo/sql working with MySQL
author | Adam Chlipala <adamc@hcoop.net> |
---|---|
date | Sun, 12 Jul 2009 15:42:24 -0400 |
parents | 3c7b48040dcf |
children | 025806b3c014 |
comparison
equal
deleted
inserted
replaced
874:3c7b48040dcf | 875:c50101ddf7fa |
---|---|
739 newline, | 739 newline, |
740 p_list_sepi (box []) (fn i => fn t => | 740 p_list_sepi (box []) (fn i => fn t => |
741 let | 741 let |
742 fun buffers t = | 742 fun buffers t = |
743 case t of | 743 case t of |
744 String => box [] | 744 String => box [string "out[", |
745 | Blob => box [] | 745 string (Int.toString i), |
746 string "].length = &length", | |
747 string (Int.toString i), | |
748 string ";", | |
749 newline] | |
750 | Blob => box [string "out[", | |
751 string (Int.toString i), | |
752 string "].length = &length", | |
753 string (Int.toString i), | |
754 string ";", | |
755 newline] | |
746 | _ => box [string "out[", | 756 | _ => box [string "out[", |
747 string (Int.toString i), | 757 string (Int.toString i), |
748 string "].buffer = &buffer", | 758 string "].buffer = &buffer", |
749 string (Int.toString i), | 759 string (Int.toString i), |
750 string ";", | 760 string ";", |
768 | _ => buffers t, | 778 | _ => buffers t, |
769 newline] | 779 newline] |
770 end) cols, | 780 end) cols, |
771 newline, | 781 newline, |
772 | 782 |
783 string "if (mysql_stmt_reset(stmt)) uw_error(ctx, FATAL, \"", | |
784 string (ErrorMsg.spanToString loc), | |
785 string ": Error reseting statement: %s\\n%s\", ", | |
786 query, | |
787 string ", mysql_error(conn->conn));", | |
788 newline, | |
789 newline, | |
790 | |
773 string "if (mysql_stmt_execute(stmt)) uw_error(ctx, FATAL, \"", | 791 string "if (mysql_stmt_execute(stmt)) uw_error(ctx, FATAL, \"", |
774 string (ErrorMsg.spanToString loc), | 792 string (ErrorMsg.spanToString loc), |
775 string ": Error executing query: %s\", mysql_error(conn->conn));", | 793 string ": Error executing query: %s\\n%s\", ", |
794 query, | |
795 string ", mysql_error(conn->conn));", | |
796 newline, | |
797 newline, | |
798 | |
799 string "if (mysql_stmt_bind_result(stmt, out)) uw_error(ctx, FATAL, \"", | |
800 string (ErrorMsg.spanToString loc), | |
801 string ": Error binding query result: %s\\n%s\", ", | |
802 query, | |
803 string ", mysql_error(conn->conn));", | |
776 newline, | 804 newline, |
777 newline, | 805 newline, |
778 | 806 |
779 string "if (mysql_stmt_store_result(stmt)) uw_error(ctx, FATAL, \"", | 807 string "if (mysql_stmt_store_result(stmt)) uw_error(ctx, FATAL, \"", |
780 string (ErrorMsg.spanToString loc), | 808 string (ErrorMsg.spanToString loc), |
781 string ": Error storing query result: %s\", mysql_error(conn->conn));", | 809 string ": Error storing query result: %s\\n%s\", ", |
782 newline, | 810 query, |
783 newline, | 811 string ", mysql_error(conn->conn));", |
784 | |
785 string "if (mysql_stmt_bind_result(stmt, out)) uw_error(ctx, FATAL, \"", | |
786 string (ErrorMsg.spanToString loc), | |
787 string ": Error binding query result: %s\", mysql_error(conn->conn));", | |
788 newline, | 812 newline, |
789 newline, | 813 newline, |
790 | 814 |
791 string "uw_end_region(ctx);", | 815 string "uw_end_region(ctx);", |
792 newline, | 816 newline, |
793 string "while ((r = mysql_stmt_fetch(stmt)) == 0) {", | 817 string "while (1) {", |
818 newline, | |
819 string "r = mysql_stmt_fetch(stmt);", | |
820 newline, | |
821 string "if (r != 0 && r != MYSQL_DATA_TRUNCATED) break;", | |
794 newline, | 822 newline, |
795 doCols p_getcol, | 823 doCols p_getcol, |
796 string "}", | 824 string "}", |
797 newline, | 825 newline, |
798 newline, | 826 newline, |
799 | 827 |
800 string "if (r == 1) uw_error(ctx, FATAL, \"", | 828 string "if (r == 1) uw_error(ctx, FATAL, \"", |
801 string (ErrorMsg.spanToString loc), | 829 string (ErrorMsg.spanToString loc), |
802 string ": query result fetching failed (%d): %s\", r, mysql_error(conn->conn));", | 830 string ": query result fetching failed: %s\\n%s\", ", |
803 newline] | 831 query, |
832 string ", mysql_error(conn->conn));", | |
833 newline, | |
834 newline, | |
835 | |
836 string "if (mysql_stmt_reset(stmt)) uw_error(ctx, FATAL, \"", | |
837 string (ErrorMsg.spanToString loc), | |
838 string ": Error reseting statement: %s\\n%s\", ", | |
839 query, | |
840 string ", mysql_error(conn->conn));", | |
841 newline, | |
842 newline] | |
804 | 843 |
805 fun query {loc, cols, doCols} = | 844 fun query {loc, cols, doCols} = |
806 box [string "uw_conn *conn = uw_get_db(ctx);", | 845 box [string "uw_conn *conn = uw_get_db(ctx);", |
807 newline, | 846 newline, |
808 string "MYSQL_stmt *stmt = mysql_stmt_init(conn->conn);", | 847 string "MYSQL_stmt *stmt = mysql_stmt_init(conn->conn);", |
809 newline, | 848 newline, |
810 string "if (stmt == NULL) uw_error(ctx, \"", | 849 string "if (stmt == NULL) uw_error(ctx, FATAL, \"", |
811 string (ErrorMsg.spanToString loc), | 850 string (ErrorMsg.spanToString loc), |
812 string ": can't allocate temporary prepared statement\");", | 851 string ": can't allocate temporary prepared statement\");", |
813 newline, | 852 newline, |
814 string "uw_push_cleanup(ctx, (void (*)(void *))mysql_stmt_close, stmt);", | 853 string "uw_push_cleanup(ctx, (void (*)(void *))mysql_stmt_close, stmt);", |
815 newline, | 854 newline, |
816 string "if (mysql_stmt_prepare(stmt, query, strlen(query))) uw_error(ctx, FATAL, \"", | 855 string "if (mysql_stmt_prepare(stmt, query, strlen(query))) uw_error(ctx, FATAL, \"", |
817 string (ErrorMsg.spanToString loc), | 856 string (ErrorMsg.spanToString loc), |
818 string ": error preparing statement: %s\", mysql_error(conn->conn));", | 857 string ": error preparing statement: %s\\n%s\", query, mysql_error(conn->conn));", |
819 newline, | 858 newline, |
820 newline, | |
821 | |
822 p_list_sepi (box []) (fn i => fn t => | |
823 let | |
824 fun buffers t = | |
825 case t of | |
826 String => box [] | |
827 | Blob => box [] | |
828 | _ => box [string "out[", | |
829 string (Int.toString i), | |
830 string "].buffer = &buffer", | |
831 string (Int.toString i), | |
832 string ";", | |
833 newline] | |
834 in | |
835 box [string "in[", | |
836 string (Int.toString i), | |
837 string "].buffer_type = ", | |
838 string (p_buffer_type t), | |
839 string ";", | |
840 newline, | |
841 | |
842 case t of | |
843 Nullable t => box [string "in[", | |
844 string (Int.toString i), | |
845 string "].is_null = &is_null", | |
846 string (Int.toString i), | |
847 string ";", | |
848 newline, | |
849 buffers t] | |
850 | _ => buffers t, | |
851 newline] | |
852 end) cols, | |
853 newline, | 859 newline, |
854 | 860 |
855 queryCommon {loc = loc, cols = cols, doCols = doCols, query = string "query"}, | 861 queryCommon {loc = loc, cols = cols, doCols = doCols, query = string "query"}, |
856 | 862 |
857 string "uw_pop_cleanup(ctx);", | 863 string "uw_pop_cleanup(ctx);", |
858 newline] | 864 newline] |
859 | |
860 fun p_ensql t e = | |
861 case t of | |
862 Int => box [string "uw_Basis_attrifyInt(ctx, ", e, string ")"] | |
863 | Float => box [string "uw_Basis_attrifyFloat(ctx, ", e, string ")"] | |
864 | String => e | |
865 | Bool => box [string "(", e, string " ? \"TRUE\" : \"FALSE\")"] | |
866 | Time => box [string "uw_Basis_attrifyTime(ctx, ", e, string ")"] | |
867 | Blob => box [e, string ".data"] | |
868 | Channel => box [string "uw_Basis_attrifyChannel(ctx, ", e, string ")"] | |
869 | Client => box [string "uw_Basis_attrifyClient(ctx, ", e, string ")"] | |
870 | Nullable String => e | |
871 | Nullable t => box [string "(", | |
872 e, | |
873 string " == NULL ? NULL : ", | |
874 p_ensql t (box [string "(*", e, string ")"]), | |
875 string ")"] | |
876 | 865 |
877 fun queryPrepared {loc, id, query, inputs, cols, doCols} = | 866 fun queryPrepared {loc, id, query, inputs, cols, doCols} = |
878 box [string "uw_conn *conn = uw_get_db(ctx);", | 867 box [string "uw_conn *conn = uw_get_db(ctx);", |
879 newline, | 868 newline, |
880 string "MYSQL_BIND in[", | 869 string "MYSQL_BIND in[", |
1050 | _ => buffers t, | 1039 | _ => buffers t, |
1051 newline] | 1040 newline] |
1052 end) inputs, | 1041 end) inputs, |
1053 newline, | 1042 newline, |
1054 | 1043 |
1044 string "if (mysql_stmt_bind_param(stmt, in)) uw_error(ctx, FATAL, \"", | |
1045 string (ErrorMsg.spanToString loc), | |
1046 string ": error binding parameters\");", | |
1047 newline, | |
1048 | |
1055 queryCommon {loc = loc, cols = cols, doCols = doCols, query = box [string "\"", | 1049 queryCommon {loc = loc, cols = cols, doCols = doCols, query = box [string "\"", |
1056 string (String.toString query), | 1050 string (String.toString query), |
1057 string "\""]}] | 1051 string "\""]}] |
1058 | 1052 |
1059 fun dml _ = box [] | 1053 fun dmlCommon {loc, dml} = |
1060 fun dmlPrepared _ = box [] | 1054 box [string "if (mysql_stmt_execute(stmt)) uw_error(ctx, FATAL, \"", |
1055 string (ErrorMsg.spanToString loc), | |
1056 string ": Error executing DML: %s\\n%s\", ", | |
1057 dml, | |
1058 string ", mysql_error(conn->conn));", | |
1059 newline, | |
1060 newline] | |
1061 | |
1062 fun dml loc = | |
1063 box [string "uw_conn *conn = uw_get_db(ctx);", | |
1064 newline, | |
1065 string "MYSQL_stmt *stmt = mysql_stmt_init(conn->conn);", | |
1066 newline, | |
1067 string "if (stmt == NULL) uw_error(ctx, \"", | |
1068 string (ErrorMsg.spanToString loc), | |
1069 string ": can't allocate temporary prepared statement\");", | |
1070 newline, | |
1071 string "uw_push_cleanup(ctx, (void (*)(void *))mysql_stmt_close, stmt);", | |
1072 newline, | |
1073 string "if (mysql_stmt_prepare(stmt, dml, strlen(dml))) uw_error(ctx, FATAL, \"", | |
1074 string (ErrorMsg.spanToString loc), | |
1075 string ": error preparing statement: %s\\n%s\", dml, mysql_error(conn->conn));", | |
1076 newline, | |
1077 newline, | |
1078 | |
1079 dmlCommon {loc = loc, dml = string "dml"}, | |
1080 | |
1081 string "uw_pop_cleanup(ctx);", | |
1082 newline] | |
1083 | |
1084 fun dmlPrepared {loc, id, dml, inputs} = | |
1085 box [string "uw_conn *conn = uw_get_db(ctx);", | |
1086 newline, | |
1087 string "MYSQL_BIND in[", | |
1088 string (Int.toString (length inputs)), | |
1089 string "];", | |
1090 newline, | |
1091 p_list_sepi (box []) (fn i => fn t => | |
1092 let | |
1093 fun buffers t = | |
1094 case t of | |
1095 String => box [string "unsigned long in_length", | |
1096 string (Int.toString i), | |
1097 string ";", | |
1098 newline] | |
1099 | Blob => box [string "unsigned long in_length", | |
1100 string (Int.toString i), | |
1101 string ";", | |
1102 newline] | |
1103 | Time => box [string (p_sql_ctype t), | |
1104 space, | |
1105 string "in_buffer", | |
1106 string (Int.toString i), | |
1107 string ";", | |
1108 newline] | |
1109 | _ => box [] | |
1110 in | |
1111 box [case t of | |
1112 Nullable t => box [string "my_bool in_is_null", | |
1113 string (Int.toString i), | |
1114 string ";", | |
1115 newline, | |
1116 buffers t] | |
1117 | _ => buffers t, | |
1118 newline] | |
1119 end) inputs, | |
1120 string "MYSQL_STMT *stmt = conn->p", | |
1121 string (Int.toString id), | |
1122 string ";", | |
1123 newline, | |
1124 newline, | |
1125 | |
1126 string "memset(in, 0, sizeof in);", | |
1127 newline, | |
1128 p_list_sepi (box []) (fn i => fn t => | |
1129 let | |
1130 fun buffers t = | |
1131 case t of | |
1132 String => box [string "in[", | |
1133 string (Int.toString i), | |
1134 string "].buffer = arg", | |
1135 string (Int.toString (i + 1)), | |
1136 string ";", | |
1137 newline, | |
1138 string "in_length", | |
1139 string (Int.toString i), | |
1140 string "= in[", | |
1141 string (Int.toString i), | |
1142 string "].buffer_length = strlen(arg", | |
1143 string (Int.toString (i + 1)), | |
1144 string ");", | |
1145 newline, | |
1146 string "in[", | |
1147 string (Int.toString i), | |
1148 string "].length = &in_length", | |
1149 string (Int.toString i), | |
1150 string ";", | |
1151 newline] | |
1152 | Blob => box [string "in[", | |
1153 string (Int.toString i), | |
1154 string "].buffer = arg", | |
1155 string (Int.toString (i + 1)), | |
1156 string ".data;", | |
1157 newline, | |
1158 string "in_length", | |
1159 string (Int.toString i), | |
1160 string "= in[", | |
1161 string (Int.toString i), | |
1162 string "].buffer_length = arg", | |
1163 string (Int.toString (i + 1)), | |
1164 string ".size;", | |
1165 newline, | |
1166 string "in[", | |
1167 string (Int.toString i), | |
1168 string "].length = &in_length", | |
1169 string (Int.toString i), | |
1170 string ";", | |
1171 newline] | |
1172 | Time => | |
1173 let | |
1174 fun oneField dst src = | |
1175 box [string "in_buffer", | |
1176 string (Int.toString i), | |
1177 string ".", | |
1178 string dst, | |
1179 string " = tms.tm_", | |
1180 string src, | |
1181 string ";", | |
1182 newline] | |
1183 in | |
1184 box [string "({", | |
1185 newline, | |
1186 string "struct tm tms;", | |
1187 newline, | |
1188 string "if (localtime_r(&arg", | |
1189 string (Int.toString (i + 1)), | |
1190 string ", &tm) == NULL) uw_error(\"", | |
1191 string (ErrorMsg.spanToString loc), | |
1192 string ": error converting to MySQL time\");", | |
1193 newline, | |
1194 oneField "year" "year", | |
1195 oneField "month" "mon", | |
1196 oneField "day" "mday", | |
1197 oneField "hour" "hour", | |
1198 oneField "minute" "min", | |
1199 oneField "second" "sec", | |
1200 newline, | |
1201 string "in[", | |
1202 string (Int.toString i), | |
1203 string "].buffer = &in_buffer", | |
1204 string (Int.toString i), | |
1205 string ";", | |
1206 newline] | |
1207 end | |
1208 | |
1209 | _ => box [string "in[", | |
1210 string (Int.toString i), | |
1211 string "].buffer = &arg", | |
1212 string (Int.toString (i + 1)), | |
1213 string ";", | |
1214 newline] | |
1215 in | |
1216 box [string "in[", | |
1217 string (Int.toString i), | |
1218 string "].buffer_type = ", | |
1219 string (p_buffer_type t), | |
1220 string ";", | |
1221 newline, | |
1222 | |
1223 case t of | |
1224 Nullable t => box [string "in[", | |
1225 string (Int.toString i), | |
1226 string "].is_null = &in_is_null", | |
1227 string (Int.toString i), | |
1228 string ";", | |
1229 newline, | |
1230 string "if (arg", | |
1231 string (Int.toString (i + 1)), | |
1232 string " == NULL) {", | |
1233 newline, | |
1234 box [string "in_is_null", | |
1235 string (Int.toString i), | |
1236 string " = 1;", | |
1237 newline], | |
1238 string "} else {", | |
1239 box [case t of | |
1240 String => box [] | |
1241 | _ => | |
1242 box [string (p_sql_ctype t), | |
1243 space, | |
1244 string "arg", | |
1245 string (Int.toString (i + 1)), | |
1246 string " = *arg", | |
1247 string (Int.toString (i + 1)), | |
1248 string ";", | |
1249 newline], | |
1250 string "in_is_null", | |
1251 string (Int.toString i), | |
1252 string " = 0;", | |
1253 newline, | |
1254 buffers t, | |
1255 newline]] | |
1256 | |
1257 | _ => buffers t, | |
1258 newline] | |
1259 end) inputs, | |
1260 newline, | |
1261 | |
1262 string "if (mysql_stmt_bind_param(stmt, in)) uw_error(ctx, FATAL, \"", | |
1263 string (ErrorMsg.spanToString loc), | |
1264 string ": error binding parameters\");", | |
1265 newline, | |
1266 | |
1267 dmlCommon {loc = loc, dml = box [string "\"", | |
1268 string (String.toString dml), | |
1269 string "\""]}] | |
1270 | |
1061 fun nextval _ = box [] | 1271 fun nextval _ = box [] |
1062 fun nextvalPrepared _ = box [] | 1272 fun nextvalPrepared _ = box [] |
1063 | 1273 |
1064 fun sqlifyString s = "CAST('" ^ String.translate (fn #"'" => "\\'" | 1274 fun sqlifyString s = "CAST('" ^ String.translate (fn #"'" => "\\'" |
1065 | #"\\" => "\\\\" | 1275 | #"\\" => "\\\\" |