post.c (7694B)
1 #include <stdlib.h> 2 #include <string.h> 3 #include <stdbool.h> 4 #include <inttypes.h> 5 #include <libpq-fe.h> 6 7 #include "util.h" 8 #include "post.h" 9 10 void post_init(post_t *post) { 11 post->title = NULL; 12 post->summary= NULL; 13 post->content= NULL; 14 post->date = NULL; 15 post->is_public = NULL; 16 } 17 18 void post_free(post_t *post) { 19 if (post->title != NULL) { 20 free(post->title); 21 } 22 if (post->summary != NULL) { 23 free(post->summary); 24 } 25 if (post->content != NULL) { 26 free(post->content); 27 } 28 if (post->date != NULL) { 29 free(post->date); 30 } 31 if (post->is_public != NULL) { 32 free(post->is_public); 33 } 34 } 35 36 void post_result_free(post_result_t *post_result) { 37 if (post_result->result != NULL) { 38 for (int i = 0; i < post_result->len; i++) { 39 post_t *post = &post_result->result[i]; 40 post_free(post); 41 } 42 free(post_result->result); 43 } 44 } 45 46 bool post_changed(post_t *post, post_t *changes) { 47 if (changes->title != NULL && strcmp(post->title, changes->title) != 0) { 48 return true; 49 } 50 if (changes->summary != NULL && strcmp(post->summary, changes->summary) != 0) { 51 return true; 52 } 53 if (changes->content != NULL && strcmp(post->content, changes->content) != 0) { 54 return true; 55 } 56 if (changes->date != NULL && strcmp(post->date, changes->date) != 0) { 57 return true; 58 } 59 if (post->is_public != NULL && *post->is_public != *changes->is_public) { 60 return true; 61 } 62 return false; 63 } 64 65 post_result_t *db_post_search(char *db, char *title) { 66 PGconn *conn = PQconnectdb(db); 67 if (PQstatus(conn) != CONNECTION_OK) { 68 printf("error: %s\n", PQerrorMessage(conn)); 69 PQfinish(conn); 70 return NULL; 71 } 72 char *query = "SELECT id, title, summary, created_at, is_public FROM posts WHERE lower(title) LIKE lower($1) ORDER BY created_at DESC;"; 73 char id_param[10]; 74 snprintf(id_param, 10, "%%%s%%", title); 75 const char *values[1] = { id_param }; 76 PGresult *result = PQexecParams(conn, query, 1, NULL, values, NULL, NULL, 0); 77 ExecStatusType status = PQresultStatus(result); 78 if (status != PGRES_TUPLES_OK) { 79 printf("error: %s\n", PQerrorMessage(conn)); 80 PQclear(result); 81 PQfinish(conn); 82 return NULL; 83 } 84 int rows = PQntuples(result); 85 int cols = PQnfields(result); 86 if (rows > 0) { 87 post_result_t *post_result = calloc(1, sizeof(post_result_t)); 88 for (int i = 0; i < rows; i++) { 89 post_result->result = realloc(post_result->result, sizeof(post_t) * (post_result->len + 1)); 90 post_t *post = &post_result->result[post_result->len]; 91 post_init(post); 92 post_result->len++; 93 for (int j = 0; j < cols; j++) { 94 char *value = PQgetvalue(result, i, j); 95 int value_len = strlen(value); 96 if (j == 0) { 97 int id = strtoimax(value, NULL, 10); 98 post->id = id; 99 } 100 if (j == 1) { 101 post->title = malloc(sizeof(char) * (value_len + 1)); 102 memcpy(post->title, value, value_len); 103 post->title[value_len] = '\0'; 104 } 105 if (j == 2) { 106 post->summary = malloc(sizeof(char) * (value_len + 1)); 107 memcpy(post->summary, value, value_len); 108 post->summary[value_len] = '\0'; 109 } 110 if (j == 3) { 111 post->date = malloc(sizeof(char) * (value_len + 1)); 112 memcpy(post->date, value, value_len); 113 post->date[value_len] = '\0'; 114 } 115 if (j == 4) { 116 if (strcmp(value, "t") == 0) { 117 post->is_public = malloc(sizeof(bool)); 118 *post->is_public = true; 119 } 120 else { 121 post->is_public = malloc(sizeof(bool)); 122 *post->is_public = false; 123 } 124 } 125 } 126 } 127 PQclear(result); 128 PQfinish(conn); 129 return post_result; 130 } 131 PQclear(result); 132 PQfinish(conn); 133 return NULL; 134 } 135 136 post_t *db_post_get(char *db, int id) { 137 PGconn *conn = PQconnectdb(db); 138 if (PQstatus(conn) != CONNECTION_OK) { 139 printf("error: %s\n", PQerrorMessage(conn)); 140 PQfinish(conn); 141 return NULL; 142 } 143 char *query = "SELECT title, summary, content, created_at, is_public FROM posts WHERE id = $1;"; 144 char id_param[10]; 145 snprintf(id_param, 10, "%d", id); 146 const char *values[1] = { id_param }; 147 PGresult *result = PQexecParams(conn, query, 1, NULL, values, NULL, NULL, 0); 148 ExecStatusType status = PQresultStatus(result); 149 if (status != PGRES_TUPLES_OK) { 150 printf("error: %s\n", PQerrorMessage(conn)); 151 PQclear(result); 152 PQfinish(conn); 153 return NULL; 154 } 155 int rows = PQntuples(result); 156 int cols = PQnfields(result); 157 if (rows > 0) { 158 post_t *post = calloc(1, sizeof(post_t)); 159 post->id = id; 160 for (int j = 0; j < cols; j++) { 161 char *value = PQgetvalue(result, 0, j); 162 int value_len = strlen(value); 163 if (j == 0) { 164 post->title = malloc(sizeof(char) * (value_len + 1)); 165 memcpy(post->title, value, value_len); 166 post->title[value_len] = '\0'; 167 } 168 if (j == 1) { 169 post->summary = malloc(sizeof(char) * (value_len + 1)); 170 memcpy(post->summary, value, value_len); 171 post->summary[value_len] = '\0'; 172 } 173 if (j == 2) { 174 post->content = malloc(sizeof(char) * (value_len + 1)); 175 memcpy(post->content, value, value_len); 176 post->content[value_len] = '\0'; 177 } 178 if (j == 3) { 179 post->date = malloc(sizeof(char) * (value_len + 1)); 180 memcpy(post->date, value, value_len); 181 post->date[value_len] = '\0'; 182 } 183 if (j == 4) { 184 if (strcmp(value, "t") == 0) { 185 post->is_public = malloc(sizeof(bool)); 186 *post->is_public = true; 187 } 188 else { 189 post->is_public = malloc(sizeof(bool)); 190 *post->is_public = false; 191 } 192 } 193 } 194 PQclear(result); 195 PQfinish(conn); 196 return post; 197 } 198 PQclear(result); 199 PQfinish(conn); 200 return NULL; 201 } 202 203 bool db_post_update(char *db, int id, post_t *post, post_t *changes) { 204 PGconn *conn = PQconnectdb(db); 205 if (PQstatus(conn) != CONNECTION_OK) { 206 printf("error: %s\n", PQerrorMessage(conn)); 207 PQfinish(conn); 208 return false; 209 } 210 char *query = NULL; 211 const char *params[10]; 212 int count = 0; 213 str_append(&query, "UPDATE posts SET"); 214 if (changes->title != NULL && strcmp(post->title, changes->title) != 0) { 215 if (count > 0) { 216 str_append(&query, ","); 217 } 218 str_appendf(&query, " title = $%d", count + 1); 219 params[count] = changes->title; 220 count++; 221 } 222 if (changes->summary != NULL && strcmp(post->summary, changes->summary) != 0) { 223 if (count > 0) { 224 str_append(&query, ","); 225 } 226 str_appendf(&query, " summary = $%d", count + 1); 227 params[count] = changes->summary; 228 count++; 229 } 230 if (changes->content != NULL && strcmp(post->content, changes->content) != 0) { 231 if (count > 0) { 232 str_appendf(&query, ","); 233 } 234 str_appendf(&query, " content = $%d", count + 1); 235 params[count] = changes->content; 236 count++; 237 } 238 if (post->is_public != NULL && *post->is_public != *changes->is_public) { 239 if (count > 0) { 240 str_appendf(&query, ","); 241 } 242 str_appendf(&query, " is_public = $%d", count + 1); 243 params[count] = *changes->is_public ? "true" : "false"; 244 count++; 245 } 246 str_appendf(&query, " WHERE id = $%d", count + 1); 247 char id_param[10]; 248 snprintf(id_param, 10, "%d", id); 249 params[count] = id_param; 250 count++; 251 PGresult *result = PQexecParams(conn, query, count, NULL, params, NULL, NULL, 0); 252 free(query); 253 ExecStatusType status = PQresultStatus(result); 254 if (status != PGRES_COMMAND_OK) { 255 printf("error: %s\n", PQerrorMessage(conn)); 256 PQclear(result); 257 PQfinish(conn); 258 return false; 259 } 260 PQclear(result); 261 PQfinish(conn); 262 return true; 263 }