so://phie

glue

A zero-boilerplate, ORM for PostgreSQL
git clone git://soophie.de/glue

blob: d9216815c6c71d939bca408914f338cf9e5e2b09 (4988 bytes)[doc][blame][raw]

1#ifndef _SO_GLUE_H

2#define _SO_GLUE_H

3

4#include <stddef.h>

5#include <stdbool.h>

6

7typedef void (*glue_fetch_fn)(char *, char *, void *);

8

9void glue_fetch(const char *query, size_t params_len, const char *params[params_len], glue_fetch_fn fn, void **data, size_t *len, size_t size);

10void glue_execute(const char *query, size_t params_len, const char *params[params_len]);

11void glue_get_string(char *src, char **dest);

12void glue_get_int(char *src, int *dest);

13void glue_get_double(char *src, double *dest);

14void glue_get_float(char *src, float *dest);

15void glue_get_bool(char *src, bool *dest);

16

17

18#define TABLEDEF($name, $cols) \

19 typedef struct { \

20 $cols(X_COLDEF) \

21 } $name##_t; \

22 \

23 void _##$name##_fetch_fn(char *column, char *value, void *data) { \

24 $name##_t *self = ($name##_t *) data; \

25 $cols(X_ASSIGN) \

26 } \

27 void $name##_fetch(const char *query, size_t params_len, const char *params[params_len], $name##_t **data, size_t *len) { \

28 glue_fetch(query, params_len, params, _##$name##_fetch_fn, (void *) data, len, sizeof($name##_t)); \

29 } \

30 void $name##_free($name##_t *self) { \

31 $cols(X_FREE) \

32 } \

33 \

34 void $name##_list_free($name##_t *list, size_t len) { \

35 if (!list) return; \

36 for (size_t i = 0; i < len; i++) { \

37 $name##_free(&list[i]); \

38 } \

39 free(list); \

40 } \

41 \

42 void $name##_dbg($name##_t *self) { \

43 fprintf(stdout, "(%s_t) {\n", #$name); \

44 $cols(X_DEBUG) \

45 fprintf(stdout, "}\n"); \

46 }

47

48#define X_COLDEF($type, $name, $getter, $freeable, $format) \

49 $type $name;

50

51#define X_FREE($type, $name, $getter, $freeable, $format) \

52 if ($freeable) { \

53 if (self->$name) { \

54 void **name = (void **) &self->$name; \

55 free(*name); \

56 *name = NULL; \

57 } \

58 }

59

60#define X_ASSIGN($type, $name, $getter, $freeable, $format) \

61 if (strcmp(column, #$name) == 0) { \

62 $getter(value, &self->$name); \

63 }

64

65#define X_DEBUG($type, $name, $getter, $freeable, $format) \

66 fprintf(stdout, " .%s = ", #$name); \

67 $format($name) \

68 fprintf(stdout, "\n");

69

70#define FORMAT_INT($name) \

71 fprintf(stdout, "%d,", (int) self->$name);

72

73#define FORMAT_TEXT($name) \

74 fprintf(stdout, "%s,", (const char *) self->$name);

75

76#define COLDEF_INT(X, $name) \

77 X(int, $name, glue_get_int, 0, FORMAT_INT)

78

79#define COLDEF_TEXT(X, $name) \

80 X(char *, $name, glue_get_string, 1, FORMAT_TEXT)

81

82#ifdef SO_GLUE_IMPL

83

84#include <stdio.h>

85#include <stdlib.h>

86#include <string.h>

87#include <inttypes.h>

88#include <libpq-fe.h>

89

90void glue_execute(const char *query, size_t params_len, const char *params[params_len]) {

91 PGconn *conn = PQconnectdb(GLUE_DB_INFO);

92 if (PQstatus(conn) != CONNECTION_OK) {

93 fprintf(stderr, "%s\n", PQerrorMessage(conn));

94 PQfinish(conn);

95 return;

96 }

97 PGresult *result = PQexecParams(conn, query, params_len, NULL, params, NULL, NULL, 0);

98 ExecStatusType status = PQresultStatus(result);

99 if (status != PGRES_COMMAND_OK) {

100 fprintf(stderr, "%s\n", PQerrorMessage(conn));

101 PQclear(result);

102 return;

103 }

104 PQclear(result);

105 PQfinish(conn);

106}

107

108void glue_fetch(const char *query, size_t params_len, const char *params[params_len], glue_fetch_fn fn, void **data, size_t *len, size_t size) {

109 PGconn *conn = PQconnectdb(GLUE_DB_INFO);

110 if (PQstatus(conn) != CONNECTION_OK) {

111 fprintf(stderr, "%s\n", PQerrorMessage(conn));

112 PQfinish(conn);

113 return;

114 }

115 PGresult *result = PQexecParams(conn, query, params_len, NULL, params, NULL, NULL, 0);

116 ExecStatusType status = PQresultStatus(result);

117 if (status != PGRES_TUPLES_OK) {

118 fprintf(stderr, "%s\n", PQerrorMessage(conn));

119 PQclear(result);

120 PQfinish(conn);

121 return;

122 }

123 size_t rows = PQntuples(result);

124 size_t cols = PQnfields(result);

125 if (rows > 0) {

126 for (size_t i = 0; i < rows; i++) {

127 void *ptr = realloc(*data, size * (*len + 1));

128 if (ptr == NULL) {

129 // TODO error message

130 PQclear(result);

131 PQfinish(conn);

132 return;

133 }

134 *data = ptr;

135 void *item = (char *) *data + size * (*len);

136 memset(item, 0, size);

137 (*len)++;

138 for (size_t j = 0; j < cols; j++) {

139 if (PQgetisnull(result, i, j)) {

140 continue;

141 }

142 char *column = PQfname(result, j);

143 char *value = PQgetvalue(result, i, j);

144 fn(column, value, item);

145 }

146 }

147 }

148 PQclear(result);

149 PQfinish(conn);

150}

151

152void glue_get_string(char *src, char **dest) {

153 size_t value_len = strlen(src);

154 if (value_len == 0) {

155 *dest = NULL;

156 }

157 *dest = malloc(sizeof(char) * (value_len + 1));

158 memcpy(*dest, src, value_len);

159 (*dest)[value_len] = '\0';

160}

161

162void glue_get_int(char *src, int *dest) {

163 *dest = strtoimax(src, NULL, 10);

164}

165

166void glue_get_double(char *src, double *dest) {

167 *dest = strtod(src, NULL);

168}

169

170void glue_get_float(char *src, float *dest) {

171 *dest = strtof(src, NULL);

172}

173

174void glue_get_bool(char *src, bool *dest) {

175 *dest = strcmp("t", src) == 0;

176}

177

178#endif /* SO_GLUE_IMPL */

179#endif /* _SO_GLUE_H */