/* autoSql - automatically generate SQL and C code for saving * and loading entities in database. * * This module is pretty ugly. It seems to work within some * limitations (described in the autoSql.doc). Rather than do major * changes from here I'd rewrite it though. The tokenizer is * pretty sound, the parser isn't bad (though the language it * parses is quirky), but the code generator is u-g-l-y. * * This file is copyright 2002-2005 Jim Kent, but license is hereby * granted for all use - public, private or commercial. */ #include "common.h" #include "errabort.h" #include "linefile.h" #include "obscure.h" #include "dystring.h" #include "asParse.h" #include "options.h" boolean withNull = FALSE; boolean makeJson = FALSE; boolean makeDjango = FALSE; void usage() /* Explain usage and exit. */ { errAbort("autoSql - create SQL and C code for permanently storing\n" "a structure in database and loading it back into memory\n" "based on a specification file\n" "usage:\n" " autoSql specFile outRoot {optional: -dbLink -withNull -json} \n" "This will create outRoot.sql outRoot.c and outRoot.h based\n" "on the contents of specFile. \n" "\n" "options:\n" " -dbLink - optionally generates code to execute queries and\n" " updates of the table.\n" " -withNull - optionally generates code and .sql to enable\n" " applications to accept and load data into objects\n" " with potential 'missing data' (NULL in SQL)\n" " situations.\n" " -django - generate method to output object as django model Python code\n" " -json - generate method to output the object in JSON (JavaScript) format.\n"); } static struct optionSpec optionSpecs[] = { {"dbLink", OPTION_BOOLEAN}, {"withNull", OPTION_BOOLEAN}, {"json", OPTION_BOOLEAN}, {"django", OPTION_BOOLEAN}, {NULL, 0} }; void sqlColumn(struct asColumn *col, FILE *f) /* Print out column in SQL. */ { fprintf(f, " %s ", col->name); struct dyString *type = asColumnToSqlType(col); fprintf(f, "%s", type->string); if (!withNull) fprintf(f, " not null"); fputc(',', f); fprintf(f, "\t# %s\n", col->comment); dyStringFree(&type); } void sqlTable(struct asObject *table, FILE *f) /* Print out structure of table in SQL. */ { struct asColumn *col; fprintf(f, "\n#%s\n", table->comment); fprintf(f, "CREATE TABLE %s (\n", table->name); for (col = table->columnList; col != NULL; col = col->next) sqlColumn(col, f); fprintf(f," #Indices\n"); fprintf(f, " PRIMARY KEY(%s)\n", table->columnList->name); fprintf(f, ");\n"); } void djangoEnumChoices(struct asColumn *col, FILE *f) /* Write out a list of choices to use with a django enum. */ { fprintf(f, " %sChoices = (\n", col->name); struct slName *val; for (val = col->values; val != NULL; val = val->next) fprintf(f, " ('%s', '%s'),\n", val->name, val->name); fprintf(f, " )\n"); } int longestValue(struct asColumn *col) /* Return length of longest value in col->values list */ { int longest = 0; struct slName *val; for (val = col->values; val != NULL; val = val->next) { int len = strlen(val->name); if (len > longest) longest = len; } return longest; } void djangoColumn(struct asColumn *col, FILE *f) /* Print out column in Django flavored python. */ { fprintf(f, " %s = models.", col->name); struct asTypeInfo *lt = col->lowType; if (lt->type == t_enum) fprintf(f, "CharField(max_length=%d choices=%sChoices)", longestValue(col), col->name); else if (lt->type == t_set) { warn("Set type fields such as '%s' are not tested in Django and are unlikely to work.", col->name); fprintf(f, "TextField() # A set in autoSql"); } else if (col->isList || col->isArray) fprintf(f, "TextField()"); else if (lt->type == t_char) fprintf(f, "CharField(max_length=%d)", col->fixedSize ? col->fixedSize : 1); else if (lt->type == t_string) fprintf(f, "CharField(max_length=255)"); else fprintf(f, "%s()", lt->djangoName); fprintf(f, "\t# %s\n", col->comment); } void djangoModel(struct asObject *table, FILE *f) /* Print out structure of table in Django flavored python */ { fprintf(f, "class %s(models.Model):\n", table->name); struct asColumn *col; for (col = table->columnList; col != NULL; col = col->next) if (col->lowType->type == t_enum) djangoEnumChoices(col, f); for (col = table->columnList; col != NULL; col = col->next) djangoColumn(col, f); fprintf(f, "\n"); } static void cSymTypePrName(struct asObject *dbObj, char *name, FILE *f) /* print the C type name, prefixed with the object name */ { char cname[1024]; safef(cname, sizeof(cname), "%s%c%s", dbObj->name, toupper(name[0]), name+1); // Fix name so that it is a valid C identifier. char *c; for (c = cname; *c != '\0'; c++) { if (!(isalnum(*c) || (*c == '_'))) *c = '_'; } fputs(cname, f); } void cSymTypeDef(struct asObject *dbObj, struct asColumn *col, FILE *f) /* print out C enum for enum or set columns. enum and value names are * prefixed with structure names to prevent collisions */ { boolean isEnum = (col->lowType->type == t_enum); fprintf(f, "enum "); cSymTypePrName(dbObj, col->name, f); fprintf(f, "\n {\n"); unsigned value = isEnum ? 0 : 1; struct slName *val; for (val = col->values; val != NULL; val = val->next) { fprintf(f, " "); cSymTypePrName(dbObj, val->name, f); if (isEnum) { fprintf(f, " = %d,\n", value); value++; } else { fprintf(f, " = 0x%.4x,\n", value); value = value << 1; } } fprintf(f, " };\n"); } void cObjColumn(struct asColumn *col, FILE *f) /* Print out an object column in C. */ { if (col->lowType->type == t_object) fprintf(f, " struct %s *%s", col->obType->name, col->name); else if (col->lowType->type == t_simple) { if (col->isArray) { if (col->fixedSize) fprintf(f, " struct %s %s[%d]", col->obType->name, col->name, col->fixedSize); else fprintf(f, " struct %s *%s", col->obType->name, col->name); } else fprintf(f, " struct %s %s", col->obType->name, col->name); } else assert(FALSE); } void cCharColumn(struct asColumn *col, FILE *f) /* Print out a char column in C. */ { fprintf(f, " %s", col->lowType->cName); if (col->fixedSize > 0) fprintf(f, " %s[%d]", col->name, col->fixedSize+1); else if (col->isList) fprintf(f, " *%s", col->name); else fprintf(f, " %s", col->name); } void cEnumColumn(struct asObject *dbObj, struct asColumn *col, FILE *f) /* print out enum column def in C */ { fprintf(f, " enum "); cSymTypePrName(dbObj, col->name, f); fprintf(f, " %s", col->name); } void cOtherColumn(struct asColumn *col, FILE *f) /* Print out other types of column in C. */ { fprintf(f, " %s", col->lowType->cName); if (!withNull) { if (!col->lowType->stringy) fputc(' ',f); } else { if (!col->lowType->stringy) { fputc(' ',f); fputc('*',f); } } if (col->isList && !col->fixedSize) fputc('*', f); fprintf(f, "%s", col->name); if (col->isList && col->fixedSize) fprintf(f, "[%d]", col->fixedSize); } void cColumn(struct asObject *dbObj, struct asColumn *col, FILE *f) /* Print out a column in C. */ { if (col->obType != NULL) cObjColumn(col, f); else if (col->lowType->type == t_char) cCharColumn(col, f); else if (col->lowType->type == t_enum) cEnumColumn(dbObj, col, f); else cOtherColumn(col, f); fprintf(f, ";\t/* %s */\n", col->comment); } void cTable(struct asObject *dbObj, FILE *f) /* Print out structure of dbObj in C. */ { struct asColumn *col; char defineName[256]; splitPath(dbObj->name, NULL, defineName, NULL); touppers(defineName); fprintf(f, "#define %s_NUM_COLS %d\n\n", defineName, slCount(dbObj->columnList)); for (col = dbObj->columnList; col != NULL; col = col->next) { if ((col->lowType->type == t_enum) || (col->lowType->type == t_set)) cSymTypeDef(dbObj, col, f); } fprintf(f, "struct %s\n", dbObj->name); fprintf(f, "/* %s */\n", dbObj->comment); fprintf(f, " {\n"); if (!dbObj->isSimple) fprintf(f, " struct %s *next; /* Next in singly linked list. */\n", dbObj->name); for (col = dbObj->columnList; col != NULL; col = col->next) cColumn(dbObj, col, f); fprintf(f, " };\n\n"); } static boolean trueFalse[] = {TRUE, FALSE}; void makeCommaInColumn(char *indent, struct asColumn *col, FILE *f, bool isArray) /* Make code to read in one column from a comma separated set. */ { struct asObject *obType = col->obType; struct asTypeInfo *lt = col->lowType; char *arrayRef = (isArray ? "[i]" : ""); if (obType != NULL) { if (lt->type == t_object) { fprintf(f, "%ss = sqlEatChar(s, '{');\n", indent); fprintf(f, "%sif(s[0] != '}')",indent); fprintf(f, "%s slSafeAddHead(&ret->%s, %sCommaIn(&s,NULL));\n", indent, col->name, obType->name); fprintf(f, "%ss = sqlEatChar(s, '}');\n", indent); fprintf(f, "%ss = sqlEatChar(s, ',');\n", indent); } else if (lt->type == t_simple) { fprintf(f, "%ss = sqlEatChar(s, '{');\n", indent); fprintf(f, "%sif(s[0] != '}')",indent); fprintf(f, "%s %sCommaIn(&s, &ret->%s%s);\n", indent, obType->name, col->name, arrayRef); fprintf(f, "%ss = sqlEatChar(s, '}');\n", indent); fprintf(f, "%ss = sqlEatChar(s, ',');\n", indent); } else { assert(FALSE); } } else if ((lt->type == t_enum) || (lt->type == t_set)) { if (!withNull) { fprintf(f, "%sret->%s = sql%sComma(&s, values_%s, &valhash_%s);\n", indent, col->name, lt->nummyName, col->name, col->name); } else { fprintf(f, "%sret->%s = needMem(sizeof(*(ret->%s)));\n", indent, col->name, col->name); fprintf(f, "%s*(ret->%s) = sql%sComma(&s, values_%s, &valhash_%s);\n", indent, col->name, lt->nummyName, col->name, col->name); } } else if (lt->stringy) fprintf(f, "%sret->%s%s = sqlStringComma(&s);\n", indent, col->name, arrayRef); else if (lt->isUnsigned) { if (!withNull) { fprintf(f, "%sret->%s%s = sqlUnsignedComma(&s);\n", indent, col->name, arrayRef); } else { fprintf(f, "%sret->%s = needMem(sizeof(unsigned));\n", indent, col->name); fprintf(f, "%s*(ret->%s%s) = sqlUnsignedComma(&s);\n", indent, col->name, arrayRef); } } else if (lt->type == t_char) { if (col->fixedSize > 0) fprintf(f, "%ssqlFixedStringComma(&s, ret->%s%s, sizeof(ret->%s%s));\n", indent, col->name, arrayRef, col->name, arrayRef); else if (col->isList) fprintf(f, "%sret->%s%s = sqlCharComma(&s);\n", indent, col->name, arrayRef); else fprintf(f, "%ssqlFixedStringComma(&s, &(ret->%s), sizeof(ret->%s));\n", indent, col->name, col->name); } else { if (!withNull) { fprintf(f, "%sret->%s%s = sql%sComma(&s);\n", indent, col->name, arrayRef, lt->nummyName); } else { fprintf(f, "%sret->%s = needMem(sizeof(*(ret->%s)));\n", indent, col->name, col->name); fprintf(f, "%s*(ret->%s%s) = sql%sComma(&s);\n", indent, col->name, arrayRef, lt->nummyName); } } } void loadColumn(struct asColumn *col, int colIx, boolean isDynamic, boolean isSizeLink, FILE *f) /* Print statement to load column. */ { char *staDyn = (isDynamic ? "Dynamic" : "Static"); if (col->isSizeLink == isSizeLink) { struct asTypeInfo *lt = col->lowType; enum asTypes type = lt->type; struct asObject *obType = col->obType; if (col->isList || col->isArray) { char *lName; if ((lName = lt->listyName) == NULL) errAbort("Sorry, lists of %s not implemented.", lt->name); if (obType) { fprintf(f, "{\n"); fprintf(f, "int i;\n"); fprintf(f, "char *s = row[%d];\n", colIx); if (col->fixedSize) { fprintf(f, "for (i=0; i<%d; ++i)\n", col->fixedSize); } else { if (type == t_simple) { fprintf(f, "AllocArray(ret->%s, ret->%s);\n", col->name, col->linkedSize->name); } fprintf(f, "for (i=0; i%s; ++i)\n", col->linkedSize->name); } fprintf(f, " {\n"); fprintf(f, " s = sqlEatChar(s, '{');\n"); if (type == t_object) { fprintf(f, " slSafeAddHead(&ret->%s, %sCommaIn(&s, NULL));\n", col->name, obType->name); } else if (type == t_simple) { fprintf(f, " %sCommaIn(&s, &ret->%s[i]);\n", obType->name, col->name); } else assert(FALSE); fprintf(f, " s = sqlEatChar(s, '}');\n"); fprintf(f, " s = sqlEatChar(s, ',');\n"); fprintf(f, " }\n"); if (type == t_object) fprintf(f, "slReverse(&ret->%s);\n", col->name); fprintf(f, "}\n"); } else { if (col->fixedSize) { if (isDynamic && lt->stringy) { fprintf(f, "{\n"); fprintf(f, "char *s = cloneString(row[%d]);\n", colIx); fprintf(f, "sql%sArray(s, ret->%s, %d);\n", lName, col->name, col->fixedSize); fprintf(f, "}\n"); } else { fprintf(f, "sql%sArray(row[%d], ret->%s, %d);\n", lName, colIx, col->name, col->fixedSize); } } else { struct asColumn *ls; fprintf(f, "{\n"); fprintf(f, "int sizeOne;\n"); fprintf(f, "sql%s%sArray(row[%d], &ret->%s, &sizeOne);\n", lName, staDyn, colIx, col->name); if ((ls = col->linkedSize) != NULL) fprintf(f, "assert(sizeOne == ret->%s);\n", ls->name); fprintf(f, "}\n"); } } } else { switch (type) { case t_float: if (!withNull) { fprintf(f, "ret->%s = sqlFloat(row[%d]);\n", col->name, colIx); } else { fprintf(f, "if (row[%d] != NULL)\n", colIx); fprintf(f, " {\n"); fprintf(f, " ret->%s = needMem(sizeof(float));\n", col->name); fprintf(f, " *(ret->%s) = sqlFloat(row[%d]);\n", col->name, colIx); fprintf(f, " }\n"); } break; case t_double: fprintf(f, "ret->%s = sqlDouble(row[%d]);\n", col->name, colIx); break; case t_string: case t_lstring: if (isDynamic) fprintf(f, "ret->%s = cloneString(row[%d]);\n", col->name, colIx); else fprintf(f, "ret->%s = row[%d];\n", col->name, colIx); break; case t_char: if (col->fixedSize > 0) fprintf(f, "safecpy(ret->%s, sizeof(ret->%s), row[%d]);\n", col->name, col->name, colIx); else fprintf(f, "ret->%s = row[%d][0];\n", col->name, colIx); break; case t_object: { struct asObject *obj = col->obType; fprintf(f, "{\n"); fprintf(f, "char *s = row[%d];\n", colIx); fprintf(f, "if(s != NULL && differentString(s, \"\"))\n"); fprintf(f, " ret->%s = %sCommaIn(&s, NULL);\n", col->name, obj->name); fprintf(f, "}\n"); break; } case t_simple: { struct asObject *obj = col->obType; fprintf(f, "{\n"); fprintf(f, "char *s = row[%d];\n", colIx); fprintf(f, "if(s != NULL && differentString(s, \"\"))\n"); fprintf(f, " %sCommaIn(&s, &ret->%s);\n", obj->name, col->name); fprintf(f, "}\n"); break; } case t_enum: case t_set: { fprintf(f, "ret->%s = sql%sParse(row[%d], values_%s, &valhash_%s);\n", col->name, col->lowType->nummyName, colIx, col->name, col->name); break; } default: { if (!withNull) { fprintf(f, "ret->%s = sql%s(row[%d]);\n", col->name, lt->nummyName, colIx); } else { fprintf(f, "if (row[%d] != NULL)\n", colIx); fprintf(f, " {\n"); fprintf(f, " ret->%s = needMem(sizeof(*(ret->%s)));\n", col->name, col->name); fprintf(f, " *(ret->%s) = sql%s(row[%d]);\n", col->name, lt->nummyName, colIx); fprintf(f, " }\n"); fprintf(f, "else\n"); fprintf(f, " {\n"); fprintf(f, " ret->%s = NULL;\n", col->name); fprintf(f, " }\n"); } break; } } } } } void makeCommaIn(struct asObject *table, FILE *f, FILE *hFile) /* Make routine that loads object from comma separated file. */ { char *tableName = table->name; struct asColumn *col; fprintf(hFile, "struct %s *%sCommaIn(char **pS, struct %s *ret);\n", tableName, tableName, tableName); fprintf(hFile, "/* Create a %s out of a comma separated string. \n", tableName); fprintf(hFile, " * This will fill in ret if non-null, otherwise will\n"); fprintf(hFile, " * return a new %s */\n\n", tableName); fprintf(f, "struct %s *%sCommaIn(char **pS, struct %s *ret)\n", tableName, tableName, tableName); fprintf(f, "/* Create a %s out of a comma separated string. \n", tableName); fprintf(f, " * This will fill in ret if non-null, otherwise will\n"); fprintf(f, " * return a new %s */\n", tableName); fprintf(f, "{\n"); fprintf(f, "char *s = *pS;\n"); fprintf(f, "\n"); fprintf(f, "if (ret == NULL)\n"); fprintf(f, " AllocVar(ret);\n"); for (col = table->columnList; col != NULL; col = col->next) { if (col->isList) { fprintf(f, "{\n"); fprintf(f, "int i;\n"); fprintf(f, "s = sqlEatChar(s, '{');\n"); if (col->fixedSize) { fprintf(f, "for (i=0; i<%d; ++i)\n", col->fixedSize); } else { if (!col->obType) fprintf(f, "AllocArray(ret->%s, ret->%s);\n", col->name, col->linkedSizeName); fprintf(f, "for (i=0; i%s; ++i)\n", col->linkedSizeName); } fprintf(f, " {\n"); makeCommaInColumn(" ", col, f, col->obType == NULL); fprintf(f, " }\n"); if (col->obType) fprintf(f, "slReverse(&ret->%s);\n", col->name); fprintf(f, "s = sqlEatChar(s, '}');\n"); fprintf(f, "s = sqlEatChar(s, ',');\n"); fprintf(f, "}\n"); } else if (col->isArray) { fprintf(f, "{\n"); fprintf(f, "int i;\n"); fprintf(f, "s = sqlEatChar(s, '{');\n"); if (col->fixedSize) { fprintf(f, "for (i=0; i<%d; ++i)\n", col->fixedSize); } else { fprintf(f, "AllocArray(ret->%s, ret->%s);\n", col->name, col->linkedSizeName); fprintf(f, "for (i=0; i%s; ++i)\n", col->linkedSizeName); } fprintf(f, " {\n"); makeCommaInColumn(" ", col, f, TRUE); fprintf(f, " }\n"); fprintf(f, "s = sqlEatChar(s, '}');\n"); fprintf(f, "s = sqlEatChar(s, ',');\n"); fprintf(f, "}\n"); } else { makeCommaInColumn("",col, f, FALSE); } } fprintf(f, "*pS = s;\n"); fprintf(f, "return ret;\n"); fprintf(f, "}\n\n"); } boolean objectHasVariableLists(struct asObject *table) /* Returns TRUE if object has any list members. */ { struct asColumn *col; for (col = table->columnList; col != NULL; col = col->next) { if ((col->isList || col->isArray) && !col->fixedSize) return TRUE; } return FALSE; } boolean objectHasSubObjects(struct asObject *table) /* Returns TRUE if object has any object members. */ { struct asColumn *col; for (col = table->columnList; col != NULL; col = col->next) { if (col->lowType->type == t_object) return TRUE; } return FALSE; } void staticLoadRow(struct asObject *table, FILE *f, FILE *hFile) /* Create C code to load a static instance from a row. * Only generated if no lists... */ { int i; char *tableName = table->name; struct asColumn *col; boolean isSizeLink; int tfIx; if (!withNull) { fprintf(hFile, "void %sStaticLoad(char **row, struct %s *ret);\n", tableName, tableName); } else { fprintf(hFile, "void %sStaticLoadWithNull(char **row, struct %s *ret);\n", tableName, tableName); } fprintf(hFile, "/* Load a row from %s table into ret. The contents of ret will\n", tableName); fprintf(hFile, " * be replaced at the next call to this function. */\n\n"); if (!withNull) { fprintf(f, "void %sStaticLoad(char **row, struct %s *ret)\n", tableName, tableName); } else { fprintf(f, "void %sStaticLoadWithNull(char **row, struct %s *ret)\n", tableName, tableName); } fprintf(f, "/* Load a row from %s table into ret. The contents of ret will\n", tableName); fprintf(f, " * be replaced at the next call to this function. */\n"); fprintf(f, "{\n"); fprintf(f, "\n"); for (tfIx = 0; tfIx < 2; ++tfIx) { isSizeLink = trueFalse[tfIx]; for (i=0,col = table->columnList; col != NULL; col = col->next, ++i) { loadColumn(col, i, FALSE, isSizeLink, f); } } fprintf(f, "}\n\n"); } void dynamicLoadRow(struct asObject *table, FILE *f, FILE *hFile) /* Create C code to load an instance from a row into dynamically * allocated memory. */ { int i; char *tableName = table->name; struct asColumn *col; boolean isSizeLink; int tfIx; if (!withNull) { fprintf(hFile, "struct %s *%sLoad(char **row);\n", tableName, tableName); } else { fprintf(hFile, "struct %s *%sLoadWithNull(char **row);\n", tableName, tableName); } fprintf(hFile, "/* Load a %s from row fetched with select * from %s\n", tableName, tableName); fprintf(hFile, " * from database. Dispose of this with %sFree(). */\n\n", tableName); if (!withNull) { fprintf(f, "struct %s *%sLoad(char **row)\n", tableName, tableName); } else { fprintf(f, "struct %s *%sLoadWithNull(char **row)\n", tableName, tableName); } fprintf(f, "/* Load a %s from row fetched with select * from %s\n", tableName, tableName); fprintf(f, " * from database. Dispose of this with %sFree(). */\n", tableName); fprintf(f, "{\n"); fprintf(f, "struct %s *ret;\n", tableName); fprintf(f, "\n"); fprintf(f, "AllocVar(ret);\n"); for (tfIx = 0; tfIx < 2; ++tfIx) { isSizeLink = trueFalse[tfIx]; for (i=0,col = table->columnList; col != NULL; col = col->next, ++i) { loadColumn(col, i, TRUE, isSizeLink, f); } } fprintf(f, "return ret;\n"); fprintf(f, "}\n\n"); } void dynamicLoadAll(struct asObject *table, FILE *f, FILE *hFile) /* Create C code to load a all objects from a tab separated file. */ { char *tableName = table->name; fprintf(hFile, "struct %s *%sLoadAll(char *fileName);\n", tableName, tableName); fprintf(hFile, "/* Load all %s from whitespace-separated file.\n", tableName); fprintf(hFile, " * Dispose of this with %sFreeList(). */\n\n", tableName); fprintf(f, "struct %s *%sLoadAll(char *fileName) \n", tableName, tableName); fprintf(f, "/* Load all %s from a whitespace-separated file.\n", tableName); fprintf(f, " * Dispose of this with %sFreeList(). */\n", tableName); fprintf(f, "{\n"); fprintf(f, "struct %s *list = NULL, *el;\n", tableName); fprintf(f, "struct lineFile *lf = lineFileOpen(fileName, TRUE);\n"); fprintf(f, "char *row[%d];\n", slCount(table->columnList)); fprintf(f, "\n"); fprintf(f, "while (lineFileRow(lf, row))\n"); fprintf(f, " {\n"); if (!withNull) { fprintf(f, " el = %sLoad(row);\n", tableName); } else { fprintf(f, " el = %sLoadWithNull(row);\n", tableName); } fprintf(f, " slAddHead(&list, el);\n"); fprintf(f, " }\n"); fprintf(f, "lineFileClose(&lf);\n"); fprintf(f, "slReverse(&list);\n"); fprintf(f, "return list;\n"); fprintf(f, "}\n\n"); } void dynamicLoadAllByChar(struct asObject *table, FILE *f, FILE *hFile) /* Create C code to load a all objects from a tab separated file. */ { char *tableName = table->name; fprintf(hFile, "struct %s *%sLoadAllByChar(char *fileName, char chopper);\n", tableName, tableName); fprintf(hFile, "/* Load all %s from chopper separated file.\n", tableName); fprintf(hFile, " * Dispose of this with %sFreeList(). */\n\n", tableName); fprintf(hFile, "#define %sLoadAllByTab(a) %sLoadAllByChar(a, '\\t');\n", tableName, tableName); fprintf(hFile, "/* Load all %s from tab separated file.\n", tableName); fprintf(hFile, " * Dispose of this with %sFreeList(). */\n\n", tableName); fprintf(f, "struct %s *%sLoadAllByChar(char *fileName, char chopper) \n", tableName, tableName); fprintf(f, "/* Load all %s from a chopper separated file.\n", tableName); fprintf(f, " * Dispose of this with %sFreeList(). */\n", tableName); fprintf(f, "{\n"); fprintf(f, "struct %s *list = NULL, *el;\n", tableName); fprintf(f, "struct lineFile *lf = lineFileOpen(fileName, TRUE);\n"); fprintf(f, "char *row[%d];\n", slCount(table->columnList)); fprintf(f, "\n"); fprintf(f, "while (lineFileNextCharRow(lf, chopper, row, ArraySize(row)))\n"); fprintf(f, " {\n"); if (!withNull) { fprintf(f, " el = %sLoad(row);\n", tableName); } else { fprintf(f, " el = %sLoadWithNull(row);\n", tableName); } fprintf(f, " slAddHead(&list, el);\n"); fprintf(f, " }\n"); fprintf(f, "lineFileClose(&lf);\n"); fprintf(f, "slReverse(&list);\n"); fprintf(f, "return list;\n"); fprintf(f, "}\n\n"); } void dynamicLoadByQueryPrintPrototype(char *tableName, FILE *f, boolean addSemi) /* Print out function prototype and opening comment. */ { fprintf(f, "struct %s *%sLoadByQuery(struct sqlConnection *conn, char *query)%s\n", tableName, tableName, (addSemi ? ";" : "")); fprintf(f, "/* Load all %s from table that satisfy the query given. \n", tableName); fprintf(f, " * Where query is of the form 'select * from example where something=something'\n"); fprintf(f, " * or 'select example.* from example, anotherTable where example.something = \n"); fprintf(f, " * anotherTable.something'.\n"); fprintf(f, " * Dispose of this with %sFreeList(). */\n", tableName); } void dynamicLoadByQuery(struct asObject *table, FILE *f, FILE *hFile) /* Create C code to build a list from a query to database. */ { char *tableName = table->name; dynamicLoadByQueryPrintPrototype(tableName, hFile, TRUE); fprintf(hFile, "\n"); dynamicLoadByQueryPrintPrototype(tableName, f, FALSE); fprintf(f, "{\n"); fprintf(f, "struct %s *list = NULL, *el;\n", tableName); fprintf(f, "struct sqlResult *sr;\n"); fprintf(f, "char **row;\n"); fprintf(f, "\n"); fprintf(f, "sr = sqlGetResult(conn, query);\n"); fprintf(f, "while ((row = sqlNextRow(sr)) != NULL)\n"); fprintf(f, " {\n"); if (!withNull) { fprintf(f, " el = %sLoad(row);\n", tableName); } else { fprintf(f, " el = %sLoadWithNull(row);\n", tableName); } fprintf(f, " slAddHead(&list, el);\n"); fprintf(f, " }\n"); fprintf(f, "slReverse(&list);\n"); fprintf(f, "sqlFreeResult(&sr);\n"); fprintf(f, "return list;\n"); fprintf(f, "}\n\n"); } void dynamicSaveToDbPrintPrototype(char *tableName, FILE *f, boolean addSemi) /* Print out function prototype and opening comment. */ { fprintf(f, "void %sSaveToDb(struct sqlConnection *conn, struct %s *el, char *tableName, int updateSize)%s\n", tableName, tableName, (addSemi ? ";" : "")); fprintf(f, "/* Save %s as a row to the table specified by tableName. \n", tableName); fprintf(f, " * As blob fields may be arbitrary size updateSize specifies the approx size\n"); fprintf(f, " * of a string that would contain the entire query. Arrays of native types are\n"); fprintf(f, " * converted to comma separated strings and loaded as such, User defined types are\n"); fprintf(f, " * inserted as NULL. Note that strings must be escaped to allow insertion into the database.\n"); fprintf(f, " * For example \"autosql's features include\" --> \"autosql\\'s features include\" \n"); fprintf(f, " * If worried about this use %sSaveToDbEscaped() */\n", tableName); } boolean lastArrayType(struct asColumn *colList) /* if there are any more string types returns TRUE else returns false */ { struct asColumn *col; for(col = colList; col != NULL; col = col->next) { struct asTypeInfo *lt = col->lowType; enum asTypes type = lt->type; if((col->isArray || col->isList) && type != t_object && type != t_simple) return FALSE; } return TRUE; } boolean noMoreColumnsToInsert(struct asColumn *colList) { struct asColumn *col; for(col = colList; col != NULL; col = col->next) { if(col->obType == NULL) return FALSE; } return TRUE; } void dynamicSaveToDb(struct asObject *table, FILE *f, FILE *hFile) /* create C code that will save a table structure to the database */ { char *tableName = table->name; struct asColumn *col; struct dyString *colInsert = newDyString(1024); /* code to associate columns with printf format characters the insert statement */ struct dyString *stringDeclarations = newDyString(1024); /* code to declare necessary strings */ struct dyString *stringFrees = newDyString(1024); /* code to free necessary strings */ struct dyString *update = newDyString(1024); /* code to do the update statement itself */ struct dyString *stringArrays = newDyString(1024); /* code to convert arrays to strings */ boolean hasArray = FALSE; dynamicSaveToDbPrintPrototype(tableName, hFile, TRUE); fprintf(hFile, "\n"); dynamicSaveToDbPrintPrototype(tableName, f, FALSE); fprintf(f, "{\n"); fprintf(f, "struct dyString *update = newDyString(updateSize);\n"); dyStringPrintf(update, "dyStringPrintf(update, \"insert into %%s values ( "); dyStringPrintf(stringDeclarations, "char "); for (col = table->columnList; col != NULL; col = col->next) { char *colName = col->name; char *outString = NULL; /* printf formater for column, i.e. %d for int, '%s' for string */ struct asTypeInfo *lt = col->lowType; enum asTypes type = lt->type; char colInsertBuff[256]; /* what variable name matches up with the printf format character in outString */ boolean colInsertFlag = TRUE; /* if column is not a native type insert NULL with no associated variable */ switch(type) { case t_char: outString = (col->fixedSize > 0) ? "'%s'" : "'%c'"; break; case t_string: outString = "'%s'"; break; default: outString = lt->outFormat; break; } switch(type) { case t_char: case t_string: case t_lstring: sprintf(colInsertBuff, " el->%s", colName); break; default: if (withNull) sprintf(colInsertBuff, " *(el->%s)", colName); else sprintf(colInsertBuff, " el->%s", colName); break; } /* it gets pretty ugly here as we have to handle arrays of objects.. */ if(col->isArray || col->isList || type == t_object || type == t_simple) { /* if we have a basic array type convert it to a string representation and insert into db */ if(type != t_object && type != t_simple ) { hasArray = TRUE; outString = "'%s'"; /* if this is the last array put a semi otherwise a comment */ if(lastArrayType(col->next)) dyStringPrintf(stringDeclarations, " *%sArray;", colName); else dyStringPrintf(stringDeclarations, " *%sArray,", colName); /* set up call to convert array to char * */ if(col->fixedSize) dyStringPrintf(stringArrays, "%sArray = sql%sArrayToString(el->%s, %d);\n", colName, lt->listyName, colName, col->fixedSize); else dyStringPrintf(stringArrays, "%sArray = sql%sArrayToString(el->%s, el->%s);\n", colName, lt->listyName, colName, col->linkedSizeName); /* code to free allocated strings */ dyStringPrintf(stringFrees, "freez(&%sArray);\n", colName); sprintf(colInsertBuff, " %sArray ", colName); } /* if we have an object, or simple data type just insert NULL, * don't wrap the whole thing up into one string.*/ else { warn("The user defined type \"%s\" in table \"%s\" will be saved to the database as NULL.", col->obType->name, tableName); outString = " NULL "; colInsertFlag = FALSE; } } /* can't have a comma at the end of the list */ if(col->next == NULL) dyStringPrintf(update, "%s", outString); else dyStringPrintf(update, "%s,",outString); /* if we still have more columns to insert add a comma */ if(!noMoreColumnsToInsert(col->next)) strcat(colInsertBuff, ", "); /* if we have a column to append do so */ if(colInsertFlag) dyStringPrintf(colInsert, "%s", colInsertBuff); } if(hasArray) { fprintf(f, "%s\n", stringDeclarations->string); } fprintf(f, "%s", stringArrays->string); fprintf(f, "%s", update->string); fprintf(f, ")\", \n\ttableName, "); fprintf(f, "%s);\n", colInsert->string); fprintf(f, "sqlUpdate(conn, update->string);\n"); fprintf(f, "freeDyString(&update);\n"); if(hasArray) { fprintf(f, "%s", stringFrees->string); } fprintf(f, "}\n\n"); dyStringFree(&colInsert); dyStringFree(&stringDeclarations); dyStringFree(&stringFrees); dyStringFree(&update); } void dynamicSaveToDbEscapedPrintPrototype(char *tableName, FILE *f, boolean addSemi) /* Print out function prototype and opening comment. */ { fprintf(f, "void %sSaveToDbEscaped(struct sqlConnection *conn, struct %s *el, char *tableName, int updateSize)%s\n", tableName, tableName, (addSemi ? ";" : "")); fprintf(f, "/* Save %s as a row to the table specified by tableName. \n", tableName); fprintf(f, " * As blob fields may be arbitrary size updateSize specifies the approx size.\n"); fprintf(f, " * of a string that would contain the entire query. Automatically \n"); fprintf(f, " * escapes all simple strings (not arrays of string) but may be slower than %sSaveToDb().\n", tableName); fprintf(f, " * For example automatically copies and converts: \n"); fprintf(f, " * \"autosql's features include\" --> \"autosql\\'s features include\" \n"); fprintf(f, " * before inserting into database. */ \n"); } boolean lastStringType(struct asColumn *colList) /* if there are any more string types returns TRUE else returns false */ { struct asColumn *col; for(col = colList; col != NULL; col = col->next) { struct asTypeInfo *lt = col->lowType; enum asTypes type = lt->type; if(type == t_char || type == t_string || type == t_lstring || ((col->isArray || col->isList) && type != t_object && type != t_simple)) return FALSE; } return TRUE; } void dynamicSaveToDbEscaped(struct asObject *table, FILE *f, FILE *hFile) /* create C code that will save a table structure to the database with * all strings escaped. */ { char *tableName = table->name; struct asColumn *col; /* We need to do a lot of things with the string datatypes use * these buffers to only cycle through columns once */ struct dyString *colInsert = newDyString(1024); /* code to associate columns with printf format characters the insert statement */ struct dyString *stringDeclarations = newDyString(1024); /* code to declare necessary strings */ struct dyString *stringFrees = newDyString(1024); /* code to free necessary strings */ struct dyString *update = newDyString(1024); /* code to do the update statement itself */ struct dyString *stringEscapes = newDyString(1024); /* code to escape strings */ struct dyString *stringArrays = newDyString(1024); /* code to convert arrays to strings */ boolean hasString = FALSE; boolean hasArray = FALSE; dynamicSaveToDbEscapedPrintPrototype(tableName, hFile, TRUE); fprintf(hFile, "\n"); dynamicSaveToDbEscapedPrintPrototype(tableName, f, FALSE); fprintf(f, "{\n"); fprintf(f, "struct dyString *update = newDyString(updateSize);\n"); dyStringPrintf(update, "dyStringPrintf(update, \"insert into %%s values ( "); dyStringPrintf(stringDeclarations, "char "); /* loop through each of the columns and add things appropriately */ for (col = table->columnList; col != NULL; col = col->next) { char *colName = col->name; char *outString = NULL; /* printf formater for column, i.e. %d for int, '%s' for string */ struct asTypeInfo *lt = col->lowType; enum asTypes type = lt->type; char colInsertBuff[256]; /* what variable name matches up with the printf format character in outString */ boolean colInsertFlag = TRUE; /* if column is not a native type insert NULL with no associated variable */ switch(type) { case t_char: case t_string: case t_lstring: /* if of string type have to do all the work of declaring, escaping, * and freeing */ if(!col->isArray && !col->isList) { hasString = TRUE; outString = "'%s'"; /* code to escape strings */ dyStringPrintf(stringEscapes, "%s = sqlEscapeString(el->%s);\n", colName, colName); /* code to free strings */ dyStringPrintf(stringFrees, "freez(&%s);\n", colName); sprintf(colInsertBuff, " %s", colName); if(lastStringType(col->next)) dyStringPrintf(stringDeclarations, " *%s;", colName); else dyStringPrintf(stringDeclarations, " *%s,", colName); } break; default: outString = lt->outFormat; if (withNull) sprintf(colInsertBuff, " *(el->%s)", colName); else sprintf(colInsertBuff, " el->%s", colName); break; } if(col->isArray || col->isList || type == t_object || type == t_simple) { /* if we have a basic array type convert it to a string representation and insert into db */ if(type != t_object && type != t_simple ) { hasArray = TRUE; outString = "'%s'"; if(lastStringType(col->next) && lastArrayType(col->next)) dyStringPrintf(stringDeclarations, " *%sArray;", colName); else dyStringPrintf(stringDeclarations, " *%sArray,", colName); if(col->fixedSize) dyStringPrintf(stringArrays, "%sArray = sql%sArrayToString(el->%s, %d);\n", colName, lt->listyName, colName, col->fixedSize); else dyStringPrintf(stringArrays, "%sArray = sql%sArrayToString(el->%s, el->%s);\n", colName, lt->listyName, colName, col->linkedSizeName); dyStringPrintf(stringFrees, "freez(&%sArray);\n", colName); sprintf(colInsertBuff, " %sArray ", colName); } /* if we have an object, or simple data type just insert NULL, don't wrap the whole thing up into one string.*/ else { warn("The user defined type \"%s\" in table \"%s\" will be saved to the database as NULL.", col->obType->name, tableName); outString = " NULL "; colInsertFlag = FALSE; } } /* can't have comma at the end of the insert */ if(col->next == NULL) dyStringPrintf(update, "%s", outString); else dyStringPrintf(update, "%s,",outString); /* if we still have more columns to insert add a comma */ if(!noMoreColumnsToInsert(col->next)) strcat(colInsertBuff, ", "); /* if we have a column to append do so */ if(colInsertFlag) dyStringPrintf(colInsert, "%s", colInsertBuff); } if(hasString || hasArray) { fprintf(f, "%s\n", stringDeclarations->string); fprintf(f, "%s\n", stringEscapes->string); } fprintf(f, "%s", stringArrays->string); fprintf(f, "%s", update->string); fprintf(f, ")\", \n\ttableName, "); fprintf(f, "%s);\n", colInsert->string); fprintf(f, "sqlUpdate(conn, update->string);\n"); fprintf(f, "freeDyString(&update);\n"); if(hasString) { fprintf(f, "%s", stringFrees->string); } fprintf(f, "}\n\n"); dyStringFree(&colInsert); dyStringFree(&stringDeclarations); dyStringFree(&stringEscapes); dyStringFree(&stringFrees); dyStringFree(&update); } boolean internalsNeedFree(struct asObject *table) /* Return TRUE if the fields of a table contain strings, * dynamically sized arrays, objects, or anything else that * needs freeing. */ { struct asColumn *col; for (col = table->columnList; col != NULL; col = col->next) { struct asTypeInfo *lt = col->lowType; enum asTypes type = lt->type; struct asObject *obj; if ((obj = col->obType) != NULL) { if (type == t_object) return TRUE; else if (type == t_simple) { if (internalsNeedFree(obj)) return TRUE; if (col->isArray && !col->fixedSize) return TRUE; } } else { if (col->isList) { if (lt->stringy) return TRUE; if (!col->fixedSize) return TRUE; } else { if (lt->stringy) return TRUE; } } } return FALSE; } void printIndent(int spaces, FILE *f, char *format, ...) /* Write out to file indented by spaces. */ { va_list args; va_start(args, format); spaceOut(f, spaces); vfprintf(f, format, args); va_end(args); } void freeFields(struct asObject *table, int spaces, FILE *f) /* Write out code to free fields of a table. */ { struct asColumn *col; for (col = table->columnList; col != NULL; col = col->next) { struct asTypeInfo *lt = col->lowType; enum asTypes type = lt->type; char *colName = col->name; struct asObject *obj; if ((obj = col->obType) != NULL) { if (type == t_object) { printIndent(spaces, f, "%sFreeList(&el->%s);\n", obj->name, colName); } else if (type == t_simple) { if (internalsNeedFree(obj)) { if (col->isArray) { if (col->fixedSize) { printIndent(spaces, f, "%sFreeInternals(el->%s, ArraySize(el->%s));\n", obj->name, colName, colName); } else { printIndent(spaces, f, "%sFreeInternals(el->%s, el->%s);\n", obj->name, colName, col->linkedSizeName); printIndent(spaces, f, "freeMem(el->%s);\n", colName); } } else { printIndent(spaces, f, "%sFreeInternals(&el->%s, 1);\n", obj->name, colName); } } else { if (col->isArray && !col->fixedSize) { printIndent(spaces, f, "freeMem(el->%s);\n", colName); } } } } else { if (col->isList) { if (lt->stringy) { printIndent(spaces, f, "/* All strings in %s are allocated at once, so only need to free first. */\n", colName); printIndent(spaces, f, "if (el->%s != NULL)\n", colName); printIndent(spaces, f, " freeMem(el->%s[0]);\n", colName); } if (!col->fixedSize) { printIndent(spaces, f, "freeMem(el->%s);\n", colName); } } else { if (lt->stringy) { printIndent(spaces, f, "freeMem(el->%s);\n", colName); } } } } } void makeFreeInternals(struct asObject *table, FILE *f, FILE *hFile) /* Make a function that frees the internal parts if any of a simple object * (or array of simple objects). */ { char *tableName = table->name; fprintf(hFile, "void %sFreeInternals(struct %s *array, int count);\n", tableName, tableName); fprintf(hFile, "/* Free internals of a simple type %s (one not put on a list). */\n\n", tableName); fprintf(f, "void %sFreeInternals(struct %s *array, int count)\n", tableName, tableName); fprintf(f, "/* Free internals of a simple type %s (one not put on a list). */\n", tableName); fprintf(f, "{\n"); fprintf(f, "int i;\n"); fprintf(f, "for (i=0; iname; fprintf(hFile, "void %sFree(struct %s **pEl);\n", tableName, tableName); fprintf(hFile, "/* Free a single dynamically allocated %s such as created\n", tableName); fprintf(hFile, " * with %sLoad(). */\n\n", tableName); fprintf(f, "void %sFree(struct %s **pEl)\n", tableName, tableName); fprintf(f, "/* Free a single dynamically allocated %s such as created\n", tableName); fprintf(f, " * with %sLoad(). */\n", tableName); fprintf(f, "{\n"); fprintf(f, "struct %s *el;\n", tableName); fprintf(f, "\n"); fprintf(f, "if ((el = *pEl) == NULL) return;\n"); freeFields(table, 0, f); fprintf(f, "freez(pEl);\n"); fprintf(f, "}\n\n"); } void makeFreeList(struct asObject *table, FILE *f, FILE *hFile) /* Make function that frees a list of dynamically table. */ { char *name = table->name; fprintf(hFile, "void %sFreeList(struct %s **pList);\n", name, name); fprintf(hFile, "/* Free a list of dynamically allocated %s's */\n\n", name); fprintf(f, "void %sFreeList(struct %s **pList)\n", name, name); fprintf(f, "/* Free a list of dynamically allocated %s's */\n", name); fprintf(f, "{\n"); fprintf(f, "struct %s *el, *next;\n", name); fprintf(f, "\n"); fprintf(f, "for (el = *pList; el != NULL; el = next)\n"); fprintf(f, " {\n"); fprintf(f, " next = el->next;\n"); fprintf(f, " %sFree(&el);\n", name); fprintf(f, " }\n"); fprintf(f, "*pList = NULL;\n"); fprintf(f, "}\n\n"); } void makeArrayColOutput(struct asColumn *col, boolean mightNeedQuotes, char *outString, char *lineEnd, FILE *f) /* Make code that prints one array or list column to a tab delimited file. */ { struct asTypeInfo *lt = col->lowType; struct asObject *obType = col->obType; char *indent = ""; fprintf(f, "{\n"); fprintf(f, "int i;\n"); if (obType != NULL) { fprintf(f, "/* Loading %s list. */\n", obType->name); fprintf(f, " {\n struct %s *it = el->%s;\n", obType->name, col->name); indent = " "; } fprintf(f, "%sif (sep == ',') fputc('{',f);\n", indent); if (col->fixedSize) fprintf(f, "%sfor (i=0; i<%d; ++i)\n", indent, col->fixedSize); else fprintf(f, "%sfor (i=0; i%s; ++i)\n", indent, col->linkedSize->name); fprintf(f, "%s {\n", indent); if (lt->type == t_object) { fprintf(f, "%s fputc('{',f);\n", indent); fprintf(f, "%s %sCommaOut(it,f);\n", indent, obType->name); fprintf(f, "%s it = it->next;\n", indent); fprintf(f, "%s fputc('}',f);\n", indent); fprintf(f, "%s fputc(',',f);\n", indent); } else if (lt->type == t_simple) { fprintf(f, "%s fputc('{',f);\n", indent); fprintf(f, "%s %sCommaOut(&it[i],f);\n", indent, obType->name); fprintf(f, "%s fputc('}',f);\n", indent); fprintf(f, "%s fputc(',',f);\n", indent); } else { if (mightNeedQuotes) fprintf(f, "%s if (sep == ',') fputc('\"',f);\n", indent); fprintf(f, "%s fprintf(f, \"%s\", el->%s[i]);\n", indent, outString, col->name); if (mightNeedQuotes) fprintf(f, "%s if (sep == ',') fputc('\"',f);\n", indent); fprintf(f, "%s fputc(',', f);\n", indent); } fprintf(f, "%s }\n", indent); fprintf(f, "%sif (sep == ',') fputc('}',f);\n", indent); if (obType != NULL) fprintf(f, " }\n"); fprintf(f, "}\n"); fprintf(f, "fputc(%s,f);\n", lineEnd); } void makeArrayColJsonOutput(struct asColumn *col, boolean mightNeedQuotes, char *outString, FILE *f) /* Make code that prints one array or list column to a tab delimited file. */ { struct asTypeInfo *lt = col->lowType; struct asObject *obType = col->obType; char *indent = ""; fprintf(f, "{\n"); fprintf(f, "int i;\n"); if (obType != NULL) { fprintf(f, "/* Loading %s list. */\n", obType->name); fprintf(f, " {\n struct %s *it = el->%s;\n", obType->name, col->name); indent = " "; } fprintf(f, "%sfputc('[',f);\n", indent); if (col->fixedSize) fprintf(f, "%sfor (i=0; i<%d; ++i)\n", indent, col->fixedSize); else fprintf(f, "%sfor (i=0; i%s; ++i)\n", indent, col->linkedSize->name); fprintf(f, "%s {\n", indent); if (lt->type == t_object) { fprintf(f, "%s %sJsonOutput(it,f);\n", indent, obType->name); fprintf(f, "%s it = it->next;\n", indent); } else if (lt->type == t_simple) { fprintf(f, "%s %sJsonOutput(&it[i],f);\n", indent, obType->name); } else { if (mightNeedQuotes) fprintf(f, "%s fputc('\"',f);\n", indent); fprintf(f, "%s fprintf(f, \"%s\", el->%s[i]);\n", indent, outString, col->name); if (mightNeedQuotes) fprintf(f, "%s fputc('\"',f);\n", indent); } if (col->fixedSize) fprintf(f, "%s if (i<%d)\n", indent, (col->fixedSize)-1); else fprintf(f, "%s if (i<(el->%s)-1)\n", indent, col->linkedSize->name); fprintf(f, "%s%s fputc(',',f);\n", indent, indent); fprintf(f, "%s }\n", indent); fprintf(f, "%sfputc(']',f);\n", indent); if (obType != NULL) fprintf(f, " }\n"); fprintf(f, "}\n"); } void makeScalarColOutput(struct asColumn *col, boolean mightNeedQuotes, char *outString, char *lineEnd, FILE *f) /* Make code that prints one scalar column to a tab delimited file. */ { struct asTypeInfo *lt = col->lowType; if (lt->type == t_object) { struct asObject *obj = col->obType; fprintf(f, "if (sep == ',') fputc('{',f);\n"); fprintf(f, "if(el->%s != NULL)", col->name); fprintf(f, " %sCommaOut(el->%s,f);\n", obj->name, col->name); fprintf(f, "if (sep == ',') fputc('}',f);\n"); fprintf(f, "fputc(%s,f);\n", lineEnd); } else if (lt->type == t_simple) { struct asObject *obj = col->obType; fprintf(f, "if (sep == ',') fputc('{',f);\n"); fprintf(f, "%sCommaOut(&el->%s,f);\n", obj->name, col->name); fprintf(f, "if (sep == ',') fputc('}',f);\n"); fprintf(f, "fputc(%s,f);\n", lineEnd); } else { if (mightNeedQuotes) fprintf(f, "if (sep == ',') fputc('\"',f);\n"); if (!withNull) { fprintf(f, "fprintf(f, \"%s\", el->%s);\n", outString, col->name); } else { if (! ((lt->type == t_string) || (lt->type == t_lstring)) ) { fprintf(f, "fprintf(f, \"%s\", *(el->%s));\n", outString, col->name); } else { fprintf(f, "fprintf(f, \"%s\", el->%s);\n", outString, col->name); } } if (mightNeedQuotes) fprintf(f, "if (sep == ',') fputc('\"',f);\n"); fprintf(f, "fputc(%s,f);\n", lineEnd); } } void makeScalarColJsonOutput(struct asColumn *col, boolean mightNeedQuotes, char *outString, FILE *f) /* Make code that prints one scalar column to a tab delimited file. */ { struct asTypeInfo *lt = col->lowType; if (lt->type == t_object) { struct asObject *obj = col->obType; fprintf(f, "if(el->%s != NULL)", col->name); fprintf(f, " %sJsonOutput(el->%s,f);\n", obj->name, col->name); } else if (lt->type == t_simple) { struct asObject *obj = col->obType; fprintf(f, "%sJsonOutput(&el->%s,f);\n", obj->name, col->name); } else { if (mightNeedQuotes) fprintf(f, "fputc('\"',f);\n"); if (!withNull) { fprintf(f, "fprintf(f, \"%s\", el->%s);\n", outString, col->name); } else { if (! ((lt->type == t_string) || (lt->type == t_lstring)) ) { fprintf(f, "fprintf(f, \"%s\", *(el->%s));\n", outString, col->name); } else { fprintf(f, "fprintf(f, \"%s\", el->%s);\n", outString, col->name); } } if (mightNeedQuotes) fprintf(f, "fputc('\"',f);\n"); } } void makeSymColOutput(struct asColumn *col, char *lineEnd, FILE *f) /* Make code that prints one symbolic column to a tab delimited file. */ { fprintf(f, "if (sep == ',') fputc('\"',f);\n"); fprintf(f, "sql%sPrint(f, el->%s, values_%s);\n", col->lowType->nummyName, col->name, col->name); fprintf(f, "if (sep == ',') fputc('\"',f);\n"); fprintf(f, "fputc(%s,f);\n", lineEnd); } void makeSymColJsonOutput(struct asColumn *col, FILE *f) /* Make code that prints one symbolic column to a tab delimited file. */ { fprintf(f, "fputc('\"',f);\n"); fprintf(f, "sql%sPrint(f, el->%s, values_%s);\n", col->lowType->nummyName, col->name, col->name); fprintf(f, "fputc('\"',f);\n"); } void makeColOutput(struct asColumn *col, FILE *f) /* Make code that prints one column to a tab delimited file. */ { char *outString = NULL; struct asTypeInfo *lt = col->lowType; enum asTypes type = lt->type; char *lineEnd = (col->next != NULL ? "sep" : "lastSep"); boolean mightNeedQuotes = FALSE; switch(type) { case t_char: outString = (col->fixedSize > 0) ? "%s" : "%c"; mightNeedQuotes = TRUE; break; case t_string: case t_lstring: outString = "%s"; mightNeedQuotes = TRUE; break; default: outString = lt->outFormat; break; } if (col->isList || col->isArray) makeArrayColOutput(col, mightNeedQuotes, outString, lineEnd, f); else if ((lt->type == t_enum) || (lt->type == t_set)) makeSymColOutput(col, lineEnd, f); else makeScalarColOutput(col, mightNeedQuotes, outString, lineEnd, f); } void makeColJsonOutput(struct asColumn *col, FILE *f) /* Make code that prints one column to a tab delimited file. */ { char *outString = NULL; struct asTypeInfo *lt = col->lowType; enum asTypes type = lt->type; boolean mightNeedQuotes = FALSE; switch(type) { case t_char: outString = (col->fixedSize > 0) ? "%s" : "%c"; mightNeedQuotes = TRUE; break; case t_string: case t_lstring: outString = "%s"; mightNeedQuotes = TRUE; break; default: outString = lt->outFormat; break; } fprintf(f, "fputc('\"',f);\n"); fprintf(f, "fprintf(f,\"%s\");\n", col->name); fprintf(f, "fputc('\"',f);\n"); fprintf(f, "fputc(':',f);\n"); if (col->isList || col->isArray) makeArrayColJsonOutput(col, mightNeedQuotes, outString, f); else if ((lt->type == t_enum) || (lt->type == t_set)) makeSymColJsonOutput(col, f); else makeScalarColJsonOutput(col, mightNeedQuotes, outString, f); } void makeOutput(struct asObject *table, FILE *f, FILE *hFile) /* Make function that prints table to tab delimited file. */ { char *tableName = table->name; struct asColumn *col; fprintf(hFile, "void %sOutput(struct %s *el, FILE *f, char sep, char lastSep);\n", tableName, tableName); fprintf(hFile, "/* Print out %s. Separate fields with sep. Follow last field with lastSep. */\n\n", tableName); fprintf(f, "void %sOutput(struct %s *el, FILE *f, char sep, char lastSep) \n", tableName, tableName); fprintf(f, "/* Print out %s. Separate fields with sep. Follow last field with lastSep. */\n", tableName); fprintf(hFile, "#define %sTabOut(el,f) %sOutput(el,f,'\\t','\\n');\n", tableName, tableName); fprintf(hFile, "/* Print out %s as a line in a tab-separated file. */\n\n", tableName); fprintf(hFile, "#define %sCommaOut(el,f) %sOutput(el,f,',',',');\n", tableName, tableName); fprintf(hFile, "/* Print out %s as a comma separated list including final comma. */\n\n", tableName); fprintf(f, "{\n"); for (col = table->columnList; col != NULL; col = col->next) makeColOutput(col, f); fprintf(f, "}\n\n"); } void makeJsonOutput(struct asObject *table, FILE *f, FILE *hFile) /* Make function that prints table in JSON format. */ { char *tableName = table->name; struct asColumn *col; fprintf(hFile, "void %sJsonOutput(struct %s *el, FILE *f);\n", tableName, tableName); fprintf(hFile, "/* Print out %s in JSON format. */\n\n", tableName); fprintf(f, "void %sJsonOutput(struct %s *el, FILE *f) \n", tableName, tableName); fprintf(f, "/* Print out %s in JSON format. */\n", tableName); fprintf(f, "{\n"); fprintf(f, "fputc('{',f);\n"); for (col = table->columnList; col != NULL; col = col->next) { makeColJsonOutput(col, f); if (col->next != NULL) fprintf(f, "fputc(',',f);\n"); } fprintf(f, "fputc('}',f);\n"); fprintf(f, "}\n\n"); } void makeDjangoOutput(struct asObject *table, FILE *f, FILE *hFile) /* Make function that prints table as django model in Python code */ { char *tableName = table->name; struct asColumn *col; fprintf(hFile, "void %sDjangoOutput(struct %s *el, FILE *f);\n", tableName, tableName); fprintf(hFile, "/* Print out %s as Django model in python. */\n\n", tableName); fprintf(f, "void %sDjangoOutput(struct %s *el, FILE *f) \n", tableName, tableName); fprintf(f, "/* Print out %s as Django model in python. */\n\n", tableName); fprintf(f, "{\n"); fprintf(f, "fputc('{',f);\n"); for (col = table->columnList; col != NULL; col = col->next) { fprintf(f, "/* TBD %s */\n", col->name); // makeColJsonOutput(col, f); if (col->next != NULL) fprintf(f, "fputc(',',f);\n"); } fprintf(f, "fputc('}',f);\n"); fprintf(f, "}\n\n"); } void cSymColumnDef(struct asColumn *col, FILE *cFile) /* output definition used for parsing and formating a symbolic column field */ { struct slName *val; fprintf(cFile, "/* definitions for %s column */\n", col->name); fprintf(cFile, "static char *values_%s[] = {", col->name); for (val = col->values; val != NULL; val = val->next) fprintf(cFile, "\"%s\", ", val->name); fprintf(cFile, "NULL};\n"); fprintf(cFile, "static struct hash *valhash_%s = NULL;\n\n", col->name); } void cSymColumnDefs(struct asObject *obj, FILE *cFile) /* output definitions used for parsing and formating a symbolic column fields */ { struct asColumn *col; for (col = obj->columnList; col != NULL; col = col->next) { if ((col->lowType->type == t_enum) || (col->lowType->type == t_set)) cSymColumnDef(col, cFile); } } void genObjectCode(struct asObject *obj, boolean doDbLoadAndSave, FILE *cFile, FILE *hFile, FILE *sqlFile, FILE *djangoFile) /* output code for one object */ { cTable(obj, hFile); cSymColumnDefs(obj, cFile); if (obj->isTable) { sqlTable(obj, sqlFile); if (makeDjango) djangoModel(obj, djangoFile); if (!objectHasVariableLists(obj) && !objectHasSubObjects(obj)) staticLoadRow(obj, cFile, hFile); if(doDbLoadAndSave) { dynamicLoadByQuery(obj, cFile, hFile); dynamicSaveToDb(obj, cFile, hFile); dynamicSaveToDbEscaped(obj, cFile, hFile); } } dynamicLoadRow(obj, cFile, hFile); dynamicLoadAll(obj, cFile, hFile); dynamicLoadAllByChar(obj, cFile, hFile); makeCommaIn(obj, cFile, hFile); /*if (makeJson) makeJsonInput(obj, cFile, hFile);*/ if (obj->isSimple) { if (internalsNeedFree(obj)) makeFreeInternals(obj, cFile, hFile); } else { makeFree(obj, cFile, hFile); makeFreeList(obj, cFile, hFile); } makeOutput(obj, cFile, hFile); if (makeJson) makeJsonOutput(obj, cFile, hFile); verbose(2, "Made %s object\n", obj->name); } int main(int argc, char *argv[]) { struct asObject *objList, *obj; char *outRoot, outTail[256]; char dotC[256]; char dotH[256]; char dotSql[256]; char dotDjango[256]; FILE *cFile; FILE *hFile; FILE *sqlFile; FILE *djangoFile = NULL; char defineName[256]; boolean doDbLoadAndSave = FALSE; optionInit(&argc, argv, optionSpecs); doDbLoadAndSave = optionExists("dbLink"); withNull = optionExists("withNull"); makeJson = optionExists("json"); makeDjango = optionExists("django"); if (argc != 3) usage(); objList = asParseFile(argv[1]); outRoot = argv[2]; /* don't embed directories in files */ splitPath(outRoot, NULL, outTail, NULL); safef(dotC, sizeof(dotC), "%s.c", outRoot); cFile = mustOpen(dotC, "w"); safef(dotH, sizeof(dotH), "%s.h", outRoot); hFile = mustOpen(dotH, "w"); safef(dotSql, sizeof(dotSql), "%s.sql", outRoot); sqlFile = mustOpen(dotSql, "w"); if (makeDjango) { safef(dotDjango, sizeof(dotDjango), "%s.django", outRoot); djangoFile = mustOpen(dotDjango, "w"); } /* Print header comment in all files. */ fprintf(hFile, "/* %s.h was originally generated by the autoSql program, which also \n" " * generated %s.c and %s.sql. This header links the database and\n" " * the RAM representation of objects. */\n\n", outTail, outTail, outTail); fprintf(cFile, "/* %s.c was originally generated by the autoSql program, which also \n" " * generated %s.h and %s.sql. This module links the database and\n" " * the RAM representation of objects. */\n\n", outTail, outTail, outTail); fprintf(sqlFile, "# %s.sql was originally generated by the autoSql program, which also \n" "# generated %s.c and %s.h. This creates the database representation of\n" "# an object which can be loaded and saved from RAM in a fairly \n" "# automatic way.\n", outTail, outTail, outTail); if (makeDjango) { fprintf(djangoFile, "# %s.python was originally generated by the autoSql program, which also \n" "# generated %s.sql %s.c and %s.h. This creates the database representation of\n" "# an object which can be loaded and saved from RAM in a fairly \n" "# automatic way.\n\n", outTail, outTail, outTail, outTail); } /* Bracket H file with definition that keeps it from being included twice. */ sprintf(defineName, "%s_H", outTail); touppers(defineName); fprintf(hFile, "#ifndef %s\n", defineName); fprintf(hFile, "#define %s\n\n", defineName); if(doDbLoadAndSave) { fprintf(hFile, "#include \"jksql.h\"\n"); } /* Put the usual includes in .c file, and also include .h file we are * generating. */ fprintf(cFile, "#include \"common.h\"\n"); fprintf(cFile, "#include \"linefile.h\"\n"); fprintf(cFile, "#include \"dystring.h\"\n"); fprintf(cFile, "#include \"jksql.h\"\n"); fprintf(cFile, "#include \"%s\"\n", dotH); fprintf(cFile, "\n"); fprintf(cFile, "\n"); /* Also generate imports for django. */ if (makeDjango) { fprintf(djangoFile, "import datetime\n"); fprintf(djangoFile, "from django.db import models\n\n"); } /* Process each object in specification file and output to .c, * .h, and .sql and even Django/Python. */ for (obj = objList; obj != NULL; obj = obj->next) genObjectCode(obj, doDbLoadAndSave, cFile, hFile, sqlFile, djangoFile); fprintf(cFile, "/* -------------------------------- End autoSql Generated Code -------------------------------- */\n\n"); fprintf(hFile, "/* -------------------------------- End autoSql Generated Code -------------------------------- */\n\n"); if (makeDjango) fprintf(djangoFile, "###################### End autoSql Generated Code ######################\n\n"); /* Finish off H file bracket. */ fprintf(hFile, "#endif /* %s */\n\n", defineName); return 0; }