#include #include #include #include #include #include #include #include #include #include #define LOG_STMT "LOG: statement: " #define LOG_EXEC "LOG: execute " #define ERROR_QUERY "ERROR: " #define DETAIL_PARAMS "DETAIL: parameters: " /* * Returns the time in @t and the entry data in @e, and how long the entry is * in the return value. */ static size_t find_entry(char *s, size_t len, char **t, char **entrydata) { char *tz, *start, *end; /* DATE TIME TIMEZONE start...end\n */ *t = strchr(s, ' ') + 1; tz = strchr(*t, ' ') + 1; start = *entrydata = strchr(tz, ' ') + 1; end = strstr(start, "\n201"); if (end == NULL) { size_t i; /* Entries at the extent/disk block end are padded with NUL. */ for (i = strlen(start) + 1; start[i] == '\0' && i < len; ++i); end = start + i; } start = strndup(start, end - start); //printf("entry %ld long: %s\n", end - *e, start); *entrydata = start; return end - s + 1; } /* * Create a new query based on "LOG: execute" and "DETAIL: parameters" lines. */ static void got_params(char *q, char *params) { std::vector v; char *p = params, *next; size_t len; size_t lastparam = atoi(strrchr(q, '$') + 1); v.reserve(lastparam); next = strchr(p, '$'); while ((p = next) != NULL) { next = strchr(p + 1, '$'); len = next ? next - p : strlen(p); /* Strip the trailing comma and space. */ if (p[len - 2] == ',') len -= 2; /* 5 characters are the leading "$1 = ". $10 works too. */ v.push_back(std::string(p + 5, p + len)); if (v.size() >= lastparam) break; } #if 0 printf("%s --- %s\n", q, params); for (auto s: v) std::cout << " ==== " << s << std::endl; #endif char pname[sizeof "$123"]; snprintf(pname, sizeof pname, "$1"); p = strstr(q, "$1"); assert(p); std::string nquery(q, p); /* Format the query, substituting $n for the n-th parameter. */ for (auto i = 0U; i < v.size(); ++i) { nquery += v[i]; p += strlen(pname); /* +1 because the go from 1 and not 0, +1 == the next one. */ snprintf(pname, sizeof pname, "$%d", i + 2); next = strstr(q, pname); if (next) nquery += std::string(p, next); else { nquery += p; break; } p = next; } /* The original queries don't seem to be using them. Too bad. */ nquery += ";"; std::cout << nquery << std::endl; free(q); } /* XXX fix the backslash at the end of a string, escaping the closing ' */ static void do_work(void *_start, size_t len) { char *start = (char *)_start; char *time, *entry, *query = NULL; size_t pos = 0; puts("\\connect klasifikace"); puts("SET client_encoding = 'LATIN2';"); puts("SET standard_conforming_strings = off;"); puts("SET escape_string_warning = off;"); puts("SET datestyle TO GERMAN;"); puts(""); while (pos < len) { char *p; pos += find_entry(start + pos, len - pos, &time, &entry); /* username + PID are not important. LOG/DETAIL sort of is. */ for (p = entry; !isupper((unsigned char)p[0]); ++p); if (strncmp(p, LOG_STMT, sizeof LOG_STMT - 1) == 0) { p += sizeof LOG_STMT - 1; if (query) { printf("%s;\n", query); free(query); query = NULL; } query = strdup(p); } else if (strncmp(p, DETAIL_PARAMS, sizeof DETAIL_PARAMS - 1) == 0) { p += sizeof DETAIL_PARAMS - 1; got_params(query, p); query = NULL; } else if (strncmp(p, LOG_EXEC, sizeof LOG_EXEC - 1) == 0) { p += sizeof LOG_STMT; while (p[0] != ' ') ++p; ++p; query = strndup(p, p - (start + pos)); assert(query); } else if (strncmp(p, ERROR_QUERY, sizeof ERROR_QUERY - 1) == 0) { //printf("--__ERROR__%s;\n", query); free(query); query = NULL; } else { //printf("--other: %s\n", p); } free(entry); } } int main(void) try { int fd = open("zuska4.log", O_RDONLY); void *mem; struct stat sb; size_t len; if (fd == -1) throw "open(2)"; if (fstat(fd, &sb) == -1) throw "fstat(2)"; len = sb.st_size; mem = mmap(NULL, len, PROT_READ, MAP_SHARED, fd, 0); if (mem == MAP_FAILED) throw "mmap(2)"; madvise(mem, len, MADV_SEQUENTIAL); posix_fadvise(fd, 0, len, POSIX_FADV_SEQUENTIAL); do_work(mem, len); munmap(mem, len); close(fd); return 0; } catch (const char *e) { std::cerr << e << ": " << strerror(errno) << std::endl; return 1; }