#include #include #include #include #include #include #include "sqlite3ext.h" #include "expat.h" SQLITE_EXTENSION_INIT1 #define UNUSED_PARAMETER(x) (void)(x); #define SQLITE_INDEX_CONSTRAINT_EQ 2 #define SQLITE_INDEX_CONSTRAINT_GT 4 #define SQLITE_INDEX_CONSTRAINT_LE 8 #define SQLITE_INDEX_CONSTRAINT_LT 16 #define SQLITE_INDEX_CONSTRAINT_GE 32 #define SQLITE_INDEX_CONSTRAINT_MATCH 64 #define CCONV _stdcall #define MAX_XML_LEVELS 100 #define BUF_SIZE 1023 static const char* ENCODING="UTF-8"; static const char* DDL = "create table FOO(" "fileName HIDDEN text," "xmlString HIDDEN text," "filter HIDDEN text," "deParent text," "deChild text," "deLevel int," "isAttribute text," "ChildType text," "LineNo HIDDEN int," "Offset HIDDEN int," "ItemNo Hidden int," "parentRowid int" ")"; typedef struct xmlPC_vtab xmlPC_vtab; typedef struct xmlPC_cursor xmlPC_cursor; /* DDL defining the structure of the vtable DDL list maybe replace at VT create */ struct xmlPC_vtab { sqlite3_vtab base; sqlite3 *db; long useMatchIndex; char *vtName; char *vtDBName; int colC; char* encoding; int bufferSize; }; struct xmlPC_cursor { sqlite3_vtab_cursor base; FILE * fIn; XML_Parser pParser; XML_Parser PRIORpParser; char* szValue; char* deAttributes; int attributesToBeConsumed; int nLevel; char** naTree; int naTreeItems[MAX_XML_LEVELS]; sqlite_uint64 naTreeRowids[MAX_XML_LEVELS]; char* fileName; char* xmlString; int xmlStringConsumed; char* filter; char* deParent; char* deChild; int isAttribute; char* ChildType; int deLevel; int LineNo; int Offset; int itemWithinLevel; sqlite_uint64 parentRowid; int isFiltered; int rowToOutput; char * lastError; int parserSuspended; sqlite_uint64 rowid; int eof; }; /* zIn is either a pointer to a NULL-terminated string in memory obtained ** from malloc(), or a NULL pointer. The string pointed to by zAppend is ** added to zIn, and the result returned in memory obtained from malloc(). ** zIn, if it was not NULL, is freed. ** ** If the third argument, quote, is not '\0', then it is used as a ** quote character for zAppend. */ static char *appendText(char *zIn, char const *zAppend, char quote){ int len; int i; int nAppend = strlen(zAppend); int nIn = (zIn?strlen(zIn):0); len = nAppend+nIn+1; if( quote ){ len += 2; for(i=0; ivtName=sqlite3_mprintf("%s",argv[2]); p_vt->vtDBName=sqlite3_mprintf("%s",argv[1]); // output is always in UTF-8 but allow for other encodings on input file //US-ASCII //UTF-8 //UTF-16 //ISO-8859-1 if (argc>3) { p_vt->encoding=sqlite3_mprintf("%s",argv[3]); if (p_vt->encoding[0]='\0') { free(p_vt->encoding); p_vt->encoding=0; } }else { //p_vt->encoding=strdup(ENCODING); p_vt->encoding=NULL; } if (argc>4) { p_vt->bufferSize=atoi(argv[4]); }else { //p_vt->encoding=strdup(ENCODING); p_vt->bufferSize=BUF_SIZE; } // use MATCH to filter p_vt->useMatchIndex=1; // colC for use in BestIndex p_vt->colC=12; //MessageBox(NULL,sqlite3_mprintf("%s",p_vt->deDDL),"String value",MB_OK); //MessageBox(NULL,sqlite3_mprintf("%i",rc),"RC value",MB_OK); rc = sqlite3_declare_vtab(db, DDL); if(rc != SQLITE_OK) { vt_destructor((sqlite3_vtab*)p_vt); return SQLITE_ERROR; } else { /* Success. Set *pp_vt and return *pp_vt = &p_vt->base; */ *pp_vt=(sqlite3_vtab*)p_vt; return SQLITE_OK; } } static int vt_connect( sqlite3 *db, void *p_aux, int argc, const char *const*argv, sqlite3_vtab **pp_vt, char **pzErr ) { return vt_create(db, p_aux, argc, argv, pp_vt, pzErr); } static int vt_destroy(sqlite3_vtab *pVtab) { xmlPC_vtab *p_vt = (xmlPC_vtab *)pVtab; if (p_vt->vtDBName) sqlite3_free(p_vt->vtDBName); if (p_vt->vtName) sqlite3_free(p_vt->vtName); if (p_vt->encoding) sqlite3_free(p_vt->encoding); p_vt->vtDBName=0; p_vt->vtName=0; p_vt->encoding=0; vt_destructor(pVtab); return 0; } static int vt_disconnect(sqlite3_vtab *pVtab) { return vt_destroy(pVtab); } static int vt_open(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **pp_cursor) { xmlPC_cursor *p_cur = (xmlPC_cursor*)sqlite3_malloc(sizeof(xmlPC_cursor)); *pp_cursor = (sqlite3_vtab_cursor*)p_cur; return (p_cur ? SQLITE_OK : SQLITE_NOMEM); } static int vt_close(sqlite3_vtab_cursor *cur) { xmlPC_cursor *p_cur = (xmlPC_cursor*)cur; xmlPC_vtab *p_vt = (xmlPC_vtab*)(cur->pVtab); int i=0; // cllose xmlparse if (p_cur->lastError) { sqlite3_free(p_cur->lastError); p_cur->lastError=0; } if (p_cur->deParent) { free(p_cur->deParent); p_cur->deParent=0; } if (p_cur->deChild) { free(p_cur->deChild); p_cur->deChild=0; } if (p_cur->ChildType) { free(p_cur->ChildType); p_cur->ChildType=0; } for(i=1; inaTree[i]) { free(p_cur->naTree[i]); p_cur->naTree[i]=0; } } if (p_cur->naTree) { free(p_cur->naTree); p_cur->naTree=0; } if (p_cur->xmlString) { free(p_cur->xmlString); p_cur->xmlString=0; } if (p_cur->filter) { free(p_cur->filter); p_cur->filter=0; } if (p_cur->pParser) { XML_ParserFree(p_cur->pParser); p_cur->pParser=0; } if (p_cur) sqlite3_free(p_cur); p_cur=0; return SQLITE_OK; } static int vt_eof(sqlite3_vtab_cursor *cur) { return ((xmlPC_cursor*)cur)->eof; } void startElement(void* pData, const XML_Char* szName, const XML_Char** szaAttrs) { xmlPC_cursor *p_cur = (xmlPC_cursor*)pData; int i=0; char * szStr=0; // ignore outer element in attributes snippet if ((p_cur->PRIORpParser) && strcmp(szName,"a-t-t-r-i-butes")==0) return; // going deeper p_cur->nLevel++; // clear the data buffer if (p_cur->szValue) { free(p_cur->szValue); p_cur->szValue=0; } // stack the name for lower down use as deParent if (p_cur->naTree[p_cur->nLevel]) { free(p_cur->naTree[p_cur->nLevel]); p_cur->naTree[p_cur->nLevel]=0; } // set up a level and set name and counter to 0 and store rowid (see below) p_cur->naTree[p_cur->nLevel]=strdup(szName); p_cur->naTreeItems[p_cur->nLevel]=0; // if a filter set and not yet found // check if this element matches filter if ((p_cur->filter) && (p_cur->isFiltered==0)) { if( strcmp(szName,p_cur->filter)==0 ) { p_cur->isFiltered=1; } } // use previous level as parent if (p_cur->deParent) { free(p_cur->deParent); p_cur->deParent=0; } p_cur->deParent=strdup((const char*)p_cur->naTree[p_cur->nLevel-1]); p_cur->parentRowid=p_cur->naTreeRowids[p_cur->nLevel-1]; p_cur->naTreeItems[p_cur->nLevel-1]++; p_cur->itemWithinLevel=p_cur->naTreeItems[p_cur->nLevel-1]; p_cur->parentRowid=p_cur->naTreeRowids[p_cur->nLevel-1]; // record this element as the child if (p_cur->deChild) { free(p_cur->deChild); p_cur->deChild=0; } p_cur->deChild=strdup(szName); // if in a sub-parse to output attributes then ignore (shouldn't be any) // also ignore atributes if outside filter if ((p_cur->deAttributes==0) && p_cur->isFiltered) { for (i = 0; szaAttrs[i]; i += 2) { if (i==0) p_cur->deAttributes = strdup(""); szStr=sqlite3_mprintf("<%s>%s", szaAttrs[i], szaAttrs[i+1], szaAttrs[i]); p_cur->deAttributes=appendText(p_cur->deAttributes,szStr,'\0'); sqlite3_free(szStr); } if (p_cur->deAttributes) p_cur->deAttributes=appendText(p_cur->deAttributes,"",'\0'); if (p_cur->deAttributes) p_cur->attributesToBeConsumed=1; } // 1st deLevel will be 0 p_cur->deLevel=p_cur->nLevel -1 ; if (p_cur->ChildType) { free(p_cur->ChildType); p_cur->ChildType=0; } if (p_cur->PRIORpParser) { p_cur->LineNo=XML_GetCurrentLineNumber(p_cur->PRIORpParser); p_cur->Offset=XML_GetCurrentColumnNumber(p_cur->PRIORpParser); } else { p_cur->LineNo=XML_GetCurrentLineNumber(p_cur->pParser); p_cur->Offset=XML_GetCurrentColumnNumber(p_cur->pParser); } p_cur->ChildType=strdup("ELEMENT"); if (p_cur->isFiltered) { p_cur->parserSuspended=1; p_cur->rowid++; // store this elements rowid for use in levels below p_cur->naTreeRowids[p_cur->nLevel]=p_cur->rowid; XML_StopParser (p_cur->pParser,TRUE); } } void endElement(void* pData, const XML_Char* szName) { xmlPC_cursor *p_cur = (xmlPC_cursor*)pData; int i=0; char * szStr=0; int isFiltered=0; // ignore outer element in attributes snippet if ((p_cur->PRIORpParser) && strcmp(szName,"a-t-t-r-i-butes")==0) { // shouldn't need to .. if (p_cur->szValue) { free(p_cur->szValue); p_cur->szValue=0; } return; } // if a data value has been built // then prepare to outpiut another tuple if (p_cur->szValue) { // use current element level as parent if (p_cur->deParent) { free(p_cur->deParent); p_cur->deParent=0; } p_cur->deParent=strdup((const char*)p_cur->naTree[p_cur->nLevel]); p_cur->parentRowid=p_cur->naTreeRowids[p_cur->nLevel]; // keep DATA p_cur->itemWithinLevel same as ELEMENT // record the szStr as the child if (p_cur->deChild) { free(p_cur->deChild); p_cur->deChild=0; } p_cur->deChild=strdup(p_cur->szValue); // set level one below the current element depth p_cur->deLevel=p_cur->nLevel; // set the type to data if (p_cur->ChildType) { free(p_cur->ChildType); p_cur->ChildType=0; } if (p_cur->PRIORpParser) { p_cur->LineNo=XML_GetCurrentLineNumber(p_cur->PRIORpParser); p_cur->Offset=XML_GetCurrentColumnNumber(p_cur->PRIORpParser); } else { p_cur->LineNo=XML_GetCurrentLineNumber(p_cur->pParser); p_cur->Offset=XML_GetCurrentColumnNumber(p_cur->pParser); } p_cur->ChildType=strdup("DATA"); } // if a filter has been found // check if this end-element turns it off // use local isFiltered to hold value for Stop Parser if (p_cur->isFiltered) { isFiltered=p_cur->isFiltered; if (p_cur->filter) { if( strcmp(szName,p_cur->filter)==0 ) { p_cur->isFiltered=0; } } } // free the parent at this level in the stack if (p_cur->naTree[p_cur->nLevel]) { free(p_cur->naTree[p_cur->nLevel]); p_cur->naTree[p_cur->nLevel]=0; p_cur->naTreeItems[p_cur->nLevel]=0; } // head back to the surface p_cur->nLevel--; // if a data value then stop Parser to allow return of row if (p_cur->szValue) { free(p_cur->szValue); p_cur->szValue=0; if (isFiltered ) { p_cur->parserSuspended=1; p_cur->rowid++; XML_StopParser (p_cur->pParser,TRUE); } } } void dataXML(void* pData, const XML_Char* szValue, int nLen) { xmlPC_cursor *p_cur = (xmlPC_cursor*)pData; int i=0; char * szStr=0; szStr=malloc(sizeof(szStr[0])*(nLen+2)); // ignore data if filter not yet matched if (p_cur->isFiltered) { if ((nLen) && (szStr)) { sqlite3_snprintf(nLen+1,szStr,"%s",szValue); // ignore a final CR or LF if (szStr[nLen-1]=='\n' || szValue[nLen-1]=='\r') { szStr[nLen-1]='\0'; } szStr[nLen]='\0'; if (strlen(szStr)) { p_cur->szValue=appendText(p_cur->szValue,szStr,'\0'); } if (szStr) free(szStr); szStr=0; } } } static void resetParser(sqlite3_vtab_cursor *cur) { xmlPC_cursor *p_cur = (xmlPC_cursor*)cur; xmlPC_vtab *p_vt = (xmlPC_vtab*)(cur->pVtab); // currently any error forces EOF but might allow continue in future for sub-parsers if (p_cur->lastError) { sqlite3_free(p_cur->lastError); p_cur->lastError=0; } // reset back to main stream if a side-journey for elements has happened if (p_cur->PRIORpParser) { // cllose xmlparse if (p_cur->pParser) { XML_ParserFree(p_cur->pParser); p_cur->pParser=0; } // next push the attributes as element value pairs into a canonical XML snippet if (p_cur->deAttributes) { free(p_cur->deAttributes); p_cur->deAttributes=0; } p_cur->isAttribute=0; p_cur->pParser=p_cur->PRIORpParser; p_cur->PRIORpParser=0; } } static enum XML_Status processAttributes(sqlite3_vtab_cursor *cur) { xmlPC_cursor *p_cur = (xmlPC_cursor*)cur; xmlPC_vtab *p_vt = (xmlPC_vtab*)(cur->pVtab); enum XML_Status status; if (p_cur->lastError) { sqlite3_free(p_cur->lastError); p_cur->lastError=0; } if (p_cur->attributesToBeConsumed==0) { // shouldn't happend fo p_cur->lastError=sqlite3_mprintf("debug: Shouldn't happen"); p_cur->rowToOutput=1; return XML_STATUS_ERROR; } if (p_cur->deAttributes) { p_cur->attributesToBeConsumed=0; p_cur->isAttribute=1; // save outer main parser p_cur->PRIORpParser = p_cur->pParser; p_cur->pParser=0; // create a new parser if ((p_cur->pParser=XML_ParserCreate(NULL))==0) return 1; XML_SetStartElementHandler(p_cur->pParser, &startElement); XML_SetEndElementHandler(p_cur->pParser, &endElement); XML_SetCharacterDataHandler(p_cur->pParser, &dataXML); XML_SetUserData(p_cur->pParser,(void *)p_cur); status=XML_Parse(p_cur->pParser,p_cur->deAttributes,strlen(p_cur->deAttributes),0); switch (status) { case XML_STATUS_SUSPENDED: // parsing is suspended each time a row is ready for output p_cur->rowToOutput=1; return status; break; case XML_STATUS_OK: return status; break; case XML_STATUS_ERROR: default: p_cur->lastError=sqlite3_mprintf("fatal: %s(Line %d at offset %d): %s", p_cur->deAttributes, XML_GetCurrentLineNumber(p_cur->pParser), XML_GetCurrentColumnNumber(p_cur->pParser), XML_ErrorString(XML_GetErrorCode(p_cur->pParser))); p_cur->rowToOutput=1; return status; break; } } else { p_cur->lastError=sqlite3_mprintf("debug: Shouldn't happen, no Attrubutes!"); p_cur->rowToOutput=1; return XML_STATUS_ERROR; } } static int fetchNextBuffer(sqlite3_vtab_cursor *cur) { xmlPC_cursor *p_cur = (xmlPC_cursor*)cur; xmlPC_vtab *p_vt = (xmlPC_vtab*)(cur->pVtab); int nLen=0; enum XML_Status status; char* szBuffer; szBuffer=0; szBuffer=malloc(sizeof(szBuffer[0]) * (p_vt->bufferSize + 2)); if (szBuffer==0) return 1; szBuffer[p_vt->bufferSize]='\0'; if (!feof(p_cur->fIn)) { nLen = fread(szBuffer, sizeof(char), p_vt->bufferSize , p_cur->fIn); status=XML_Parse(p_cur->pParser, szBuffer, nLen, 0); switch (status) { case XML_STATUS_SUSPENDED: p_cur->rowToOutput=1; return 0; break; case XML_STATUS_OK: return 0; break; case XML_STATUS_ERROR: p_cur->lastError=sqlite3_mprintf("fatal: %s(Line %d at offset %d): %s", p_cur->fileName, XML_GetCurrentLineNumber(p_cur->pParser), XML_GetCurrentColumnNumber(p_cur->pParser), XML_ErrorString(XML_GetErrorCode(p_cur->pParser))); p_cur->rowToOutput=1; return 0; break; default: return 1; } } else { return 1; } } static int fetchXMLString(sqlite3_vtab_cursor *cur) { xmlPC_cursor *p_cur = (xmlPC_cursor*)cur; xmlPC_vtab *p_vt = (xmlPC_vtab*)(cur->pVtab); enum XML_Status status; if (p_cur->xmlStringConsumed) { return 1; } if (p_cur->xmlString) { p_cur->xmlStringConsumed=1; status=XML_Parse(p_cur->pParser,p_cur->xmlString,strlen(p_cur->xmlString),0); switch (status) { case XML_STATUS_SUSPENDED: // parsing is suspended each time a row is ready for output p_cur->rowToOutput=1; return 0; break; case XML_STATUS_OK: return 0; break; case XML_STATUS_ERROR: p_cur->lastError=sqlite3_mprintf("fatal: %s(Line %d at offset %d): %s", p_cur->xmlString, XML_GetCurrentLineNumber(p_cur->pParser), XML_GetCurrentColumnNumber(p_cur->pParser), XML_ErrorString(XML_GetErrorCode(p_cur->pParser))); p_cur->rowToOutput=1; return 0; break; default: return 1; } } else { return 1; } } static int vt_next(sqlite3_vtab_cursor *cur) { xmlPC_cursor *p_cur = (xmlPC_cursor*)cur; xmlPC_vtab *p_vt = (xmlPC_vtab*)(cur->pVtab); int eof=0; enum XML_Status status; //XML_ParsingStatus parsing_status; if (p_cur->lastError) { // having reported the error set EOF eof=1; } else { // p_cur->rowToOutput controls if a rwo to return // triggered by XML_ParseStop in event handlers while (p_cur->rowToOutput==0) { // need the parserSuspended indicator as 1st time around ResumeParser // will not return XML_ERROR_NOT_SUSPENDED if (p_cur->parserSuspended) { // if attributes found then change to a sub parser to // process these if (p_cur->attributesToBeConsumed) { p_cur->parserSuspended=0; status=processAttributes(cur); } else { p_cur->parserSuspended=0; status=XML_ResumeParser(p_cur->pParser); } } else { status=XML_STATUS_OK; } switch (status) { // first time and when a read buffer contains nothing of interest yet case XML_ERROR_NOT_SUSPENDED: case XML_STATUS_OK: // if a // fetch next buffer of data if (p_cur->PRIORpParser) { // when finished will attributes parse, reset back to main parser // & resume parsing resetParser(cur); p_cur->parserSuspended=1; } else if (p_cur->fIn) { eof=fetchNextBuffer(cur); } else { // xmlString will return EOF on 2nd call eof=fetchXMLString(cur); } break; case XML_STATUS_ERROR: if (p_cur->lastError==0) { if (p_cur->PRIORpParser) { p_cur->lastError=sqlite3_mprintf("fatal: %s(Line %d at offset %d): %s", p_cur->deAttributes, XML_GetCurrentLineNumber(p_cur->pParser), XML_GetCurrentColumnNumber(p_cur->pParser), XML_ErrorString(XML_GetErrorCode(p_cur->pParser))); } else if (p_cur->fIn) { p_cur->lastError=sqlite3_mprintf("fatal: %s(Line %d at offset %d): %s", p_cur->fileName, XML_GetCurrentLineNumber(p_cur->pParser), XML_GetCurrentColumnNumber(p_cur->pParser), XML_ErrorString(XML_GetErrorCode(p_cur->pParser))); } else { p_cur->lastError=sqlite3_mprintf("fatal: %s(Line %d at offset %d): %s", p_cur->xmlString, XML_GetCurrentLineNumber(p_cur->pParser), XML_GetCurrentColumnNumber(p_cur->pParser), XML_ErrorString(XML_GetErrorCode(p_cur->pParser))); } } p_cur->rowToOutput=1; break; case XML_STATUS_SUSPENDED: p_cur->rowToOutput=1; break; default: break; } // set rowToOutput on if EOF to force last SQLite Return if (eof) p_cur->rowToOutput=1; } } if (eof) { p_cur->eof=eof; } p_cur->rowToOutput=0; return SQLITE_OK; } static int vt_column(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int col) { xmlPC_cursor *p_cur = (xmlPC_cursor*)cur; xmlPC_vtab *p_vt = (xmlPC_vtab*)(cur->pVtab); switch(col) { case 0: { if (p_cur->fileName) { sqlite3_result_text(ctx, p_cur->fileName, -1, SQLITE_STATIC); } else sqlite3_result_null(ctx); } break; case 1: { if (p_cur->xmlString) { sqlite3_result_text(ctx, p_cur->xmlString, -1, SQLITE_STATIC); } else sqlite3_result_null(ctx); } break; case 2: { if (p_cur->filter) { sqlite3_result_text(ctx, p_cur->filter, -1, SQLITE_STATIC); } else sqlite3_result_null(ctx); } break; case 3: if (p_cur->deParent) { sqlite3_result_text(ctx, p_cur->deParent, -1, SQLITE_STATIC); } else sqlite3_result_null(ctx); break; case 4: { sqlite3_result_text(ctx, p_cur->deChild, -1, SQLITE_STATIC); } break; case 5: { sqlite3_result_int(ctx, p_cur->deLevel); } break; case 6: if (p_cur->isAttribute) { sqlite3_result_text(ctx, "Y", -1, SQLITE_STATIC); } else sqlite3_result_text(ctx, "N", -1, SQLITE_STATIC); break; case 7: if ((p_cur->ChildType) || (p_cur->lastError) ) { if (p_cur->lastError) { sqlite3_result_text(ctx, p_cur->lastError, -1, SQLITE_STATIC); } else { sqlite3_result_text(ctx, p_cur->ChildType, -1, SQLITE_STATIC); } } else sqlite3_result_null(ctx); break; case 8: { sqlite3_result_int(ctx, p_cur->LineNo); } break; case 9: { sqlite3_result_int(ctx, p_cur->Offset); } break; case 10: { sqlite3_result_int(ctx, p_cur->itemWithinLevel); } break; case 11: { sqlite3_result_int64(ctx, p_cur->parentRowid); } break; default: { sqlite3_result_null(ctx); } } return SQLITE_OK; } static int vt_rowid(sqlite3_vtab_cursor *cur, sqlite_int64 *p_rowid) { xmlPC_cursor *p_cur = (xmlPC_cursor*)cur; /* Just use the current row count as the rowid. */ *p_rowid = p_cur->rowid; return SQLITE_OK; } static int vt_filter( sqlite3_vtab_cursor *p_vtc, int idxNum, const char *idxStr, int argc, sqlite3_value **argv ) { /* Initialize the cursor structure. */ xmlPC_cursor *p_cur = (xmlPC_cursor*)p_vtc; xmlPC_vtab *p_vt = (xmlPC_vtab*)p_vtc->pVtab; int i=0; p_cur->eof=0; p_cur->rowid=0; p_cur->xmlString=0; p_cur->xmlStringConsumed=0; p_cur->fileName=0; p_cur->filter=0; p_cur->fIn=0; p_cur->deParent=0; p_cur->deChild=0; p_cur->deLevel=0; p_cur->nLevel=0; p_cur->ChildType=0; p_cur->deAttributes=0; p_cur->isAttribute=0; p_cur->attributesToBeConsumed=0; p_cur->rowid=0; p_cur->parentRowid=0; p_cur->LineNo=0; p_cur->Offset=0; p_cur->itemWithinLevel=0; p_cur->szValue=0; p_cur->rowToOutput=0; p_cur->lastError=0; p_cur->pParser=0; p_cur->PRIORpParser=0; p_cur->parserSuspended=0; p_cur->isFiltered=0; //naTree - keeps track of parent delement names p_cur->naTree=malloc( sizeof(p_cur->naTree[0])*(MAX_XML_LEVELS+1)); p_cur->naTree[0]=strdup("root"); for(i=1; inaTree[i]=0; } for(i=0; inaTreeItems[i]=0; } for(i=0; inaTreeRowids[i]=0; } if ((argc) && (idxNum)) { if (idxNum==1) { p_cur->fileName=strdup(sqlite3_value_text(argv[0])); } if (idxNum==2) { p_cur->xmlString=strdup(sqlite3_value_text(argv[0])); } if (argc==2) { p_cur->filter=strdup(sqlite3_value_text(argv[1])); } } else { p_cur->eof=1; return SQLITE_OK; } // is no filter then all are valid if (p_cur->filter==0) { p_cur->isFiltered=1; } if (p_cur->fileName) { p_cur->fIn = fopen(p_cur->fileName, "r"); if (p_cur->fIn==0) { p_cur->eof=1; return SQLITE_OK; } } //printf("filename %s \n",p_cur->fileName); if (p_cur->pParser=XML_ParserCreate(p_vt->encoding)) { XML_SetStartElementHandler(p_cur->pParser, &startElement); XML_SetEndElementHandler(p_cur->pParser, &endElement); XML_SetCharacterDataHandler(p_cur->pParser, &dataXML); XML_SetUserData(p_cur->pParser,(void *)p_cur); // printf(" here %s \n",p_cur->fileName); return vt_next(p_vtc); } else { p_cur->eof=1; return SQLITE_OK; } return SQLITE_OK; } /** Returns the index of the constraint that matches the input specifications, if * any. Returns -1 otherwise. * * Input specification: * col: index of column (as defined in virtual table schema). * opmask: a bitmask of constraint operations. * if there is an input constraint for column col which uses any of the * constraint operations in opmaks, then the function will return the index of * the input constraint, or -1 otherwise. */ int has_constraint(sqlite3_index_info *p_info, int col, int opmask, int con) { if(p_info->aConstraint[con].iColumn == col) { /* If specific operations are specified */ if(opmask != 0) { /* Check that at least one is satisfied */ // ignore pConstraint->usable if(p_info->aConstraint[con].op & opmask) { return con; } } return -1; } return -1; } static int vt_best_index(sqlite3_vtab *tab, sqlite3_index_info *p_info) { int i = 0; int j = 0; int retPos = 0; int nextPos = 0; int lColC = 0; int opsEQ = SQLITE_INDEX_CONSTRAINT_EQ; int opsMATCH = SQLITE_INDEX_CONSTRAINT_MATCH; int sourceSelected = 0; int ops = 0; //char * szStr; xmlPC_vtab *p_vt = (xmlPC_vtab*)tab; /** If their is a p1(column 0) constraint in the WHERE clause * and it uses the equals operator */ lColC = p_vt->colC; p_info->idxStr=0; p_info->needToFreeIdxStr=0; p_info->idxNum=0; //szStr=0; p_info->estimatedCost=1; ops=opsEQ; if (p_vt->useMatchIndex) ops=opsMATCH; for(i = 0; i < lColC; i++) { // retry now for EQ for(j = 0; j < p_info->nConstraint; j++) { if((retPos = has_constraint(p_info, i, ops,j)) > -1) { /* Then we want the value to be passed to xFilter() */ nextPos++; //szStr=sqlite3_mprintf("%i",i+1); // source be be either 1st column i.e. a filename if (i==0) { p_info->idxNum=1; sourceSelected=0; p_info->aConstraintUsage[retPos].argvIndex = 1; p_info->aConstraintUsage[retPos].omit = 1; } // or if a filename not matched, then a string containing the XML if ((i==1) && (sourceSelected==0)) { p_info->idxNum=2; sourceSelected=1; p_info->aConstraintUsage[retPos].argvIndex = 1; p_info->aConstraintUsage[retPos].omit = 1; } // third is the filter if ((i==2) && (sourceSelected==0)) { p_info->aConstraintUsage[retPos].argvIndex = 2; p_info->aConstraintUsage[retPos].omit = 1; } p_info->estimatedCost=1; //MessageBox(NULL,sqlite3_mprintf("col %i Predicate %i at pos %i - str %s",i,retPos,nextPos,szStr),"Cols to position",MB_OK); } } } //if (szStr) { // p_info->idxStr=szStr; // p_info->needToFreeIdxStr=1; //} return SQLITE_OK; } static void matchFunc ( sqlite3_context *pContext, int argc, sqlite3_value **argv ){ sqlite3_result_int(pContext,1); } static int vt_FindFunction( sqlite3_vtab *pVtab, int nArg, const char *zName, void (**pxFunc)(sqlite3_context*,int,sqlite3_value**), void **ppArg ){ xmlPC_vtab *p_vt = (xmlPC_vtab*)pVtab; if( strcmp(zName,"match")==0 ){ if (p_vt->useMatchIndex) { *pxFunc = matchFunc; return 1; } else { return 0; } } return 0; } static sqlite3_module xmlPC_module = { 0, /* iVersion */ vt_create, /* xCreate - create a vtable */ vt_connect, /* xConnect - associate a vtable with a connection */ vt_best_index, /* xBestIndex - best index */ vt_disconnect, /* xDisconnect - disassociate a vtable with a connection */ vt_destroy, /* xDestroy - destroy a vtable */ vt_open, /* xOpen - open a cursor */ vt_close, /* xClose - close a cursor */ vt_filter, /* xFilter - configure scan constraints */ vt_next, /* xNext - advance a cursor */ vt_eof, /* xEof - inidicate end of result set*/ vt_column, /* xColumn - read data */ vt_rowid, /* xRowid - read data */ NULL, /* xUpdate - write data */ NULL, /* xBegin - begin transaction */ NULL, /* xSync - sync transaction */ NULL, /* xCommit - commit transaction */ NULL, /* xRollback - rollback transaction */ vt_FindFunction, /* xFindFunction - function overloading */ }; int xmlPC_register(sqlite3 *db) { return sqlite3_create_module(db, "VT_xmlPC", &xmlPC_module, (void *)0); } static int initMode(sqlite3 *db) { sqlite3 *DB = 0; int rc; rc=xmlPC_register(db); return rc; } int sqlite3_extension_init( sqlite3 *db, char **pzErrMsg, const sqlite3_api_routines *pApi ) { SQLITE_EXTENSION_INIT2(pApi); if(initMode(db) != SQLITE_OK) { fprintf(stderr, "Failed to register module\n"); return SQLITE_ERROR; } return SQLITE_OK; }