#include <sys/types.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#if defined(_WIN32) && !defined(__MINGW32__)
#include "config-msvc.h"
#else
#include "config.h"
#endif
#include <spatialite/sqlite.h>
#include <spatialite/spatialite.h>
#include <spatialite/gaiaaux.h>
#include <spatialite/gaiageo.h>
#ifndef OMIT_FREEXL
#include <freexl.h>
#endif
#ifdef _WIN32
#define strcasecmp _stricmp
#endif
#ifndef OMIT_ICONV
#ifndef OMIT_FREEXL
static struct sqlite3_module my_XL_module;
typedef struct VirtualXLStruct
{
const sqlite3_module *pModule;
int nRef;
char *zErrMsg;
sqlite3 *db;
const void *XL_handle;
unsigned int rows;
unsigned short columns;
char firstLineTitles;
} VirtualXL;
typedef VirtualXL *VirtualXLPtr;
typedef struct VirtualXLConstraintStruct
{
int iColumn;
int op;
char valueType;
sqlite3_int64 intValue;
double dblValue;
char *txtValue;
struct VirtualXLConstraintStruct *next;
} VirtualXLConstraint;
typedef VirtualXLConstraint *VirtualXLConstraintPtr;
typedef struct VirtualXLCursorStruct
{
VirtualXLPtr pVtab;
unsigned int current_row;
int eof;
VirtualXLConstraintPtr firstConstraint;
VirtualXLConstraintPtr lastConstraint;
} VirtualXLCursor;
typedef VirtualXLCursor *VirtualXLCursorPtr;
static int
vXL_create (sqlite3 * db, void *pAux, int argc, const char *const *argv,
sqlite3_vtab ** ppVTab, char **pzErr)
{
char *sql;
VirtualXLPtr p_vt;
char path[2048];
char firstLineTitles = 'N';
unsigned int worksheet = 0;
unsigned int max_worksheet;
unsigned int info;
unsigned int rows;
unsigned short columns;
unsigned short col;
int len;
int ret;
const void *handle;
const char *pPath = NULL;
char *xname;
gaiaOutBuffer sql_statement;
if (pAux)
pAux = pAux;
if (argc == 4 || argc == 5 || argc == 6)
{
pPath = argv[3];
len = strlen (pPath);
if ((*(pPath + 0) == '\'' || *(pPath + 0) == '"')
&& (*(pPath + len - 1) == '\'' || *(pPath + len - 1) == '"'))
{
strcpy (path, pPath + 1);
len = strlen (path);
*(path + len - 1) = '\0';
}
else
strcpy (path, pPath);
if (argc == 5 || argc == 6)
worksheet = atoi (argv[4]);
if (argc == 6)
{
if (atoi (argv[5]) == 1)
firstLineTitles = 'Y';
}
}
else
{
*pzErr =
sqlite3_mprintf
("[VirtualXL module] CREATE VIRTUAL: illegal arg list {xls_path [, worksheet_index [, first_line_titles(1/0)]]}");
return SQLITE_ERROR;
}
p_vt = (VirtualXLPtr) sqlite3_malloc (sizeof (VirtualXL));
if (!p_vt)
return SQLITE_NOMEM;
p_vt->pModule = &my_XL_module;
p_vt->nRef = 0;
p_vt->zErrMsg = NULL;
p_vt->db = db;
p_vt->XL_handle = NULL;
p_vt->rows = 0;
p_vt->columns = 0;
p_vt->firstLineTitles = firstLineTitles;
ret = freexl_open (path, &handle);
if (ret != FREEXL_OK)
{
freexl_close (handle);
xname = gaiaDoubleQuotedSql (argv[2]);
sql = sqlite3_mprintf ("CREATE TABLE \"%s\" (PKUID INTEGER)", xname);
free (xname);
if (sqlite3_declare_vtab (db, sql) != SQLITE_OK)
{
sqlite3_free (sql);
*pzErr =
sqlite3_mprintf
("[VirtualXL module] cannot build a table from XL\n");
return SQLITE_ERROR;
}
sqlite3_free (sql);
*ppVTab = (sqlite3_vtab *) p_vt;
return SQLITE_OK;
}
freexl_get_info (handle, FREEXL_BIFF_PASSWORD, &info);
if (info != FREEXL_BIFF_PLAIN)
{
freexl_close (handle);
xname = gaiaDoubleQuotedSql (argv[2]);
sql = sqlite3_mprintf ("CREATE TABLE \"%s\" (PKUID INTEGER)", xname);
free (xname);
if (sqlite3_declare_vtab (db, sql) != SQLITE_OK)
{
sqlite3_free (sql);
*pzErr =
sqlite3_mprintf
("[VirtualXL module] Password protected [obfuscated] .xls\n");
return SQLITE_ERROR;
}
sqlite3_free (sql);
*ppVTab = (sqlite3_vtab *) p_vt;
return SQLITE_OK;
}
freexl_get_info (handle, FREEXL_BIFF_SHEET_COUNT, &max_worksheet);
if (worksheet >= max_worksheet)
{
freexl_close (handle);
xname = gaiaDoubleQuotedSql (argv[2]);
sql = sqlite3_mprintf ("CREATE TABLE \"%s\" (PKUID INTEGER)", xname);
free (xname);
if (sqlite3_declare_vtab (db, sql) != SQLITE_OK)
{
sqlite3_free (sql);
*pzErr =
sqlite3_mprintf
("[VirtualXL module] no such Worksheet [index=%u]\n",
worksheet);
return SQLITE_ERROR;
}
sqlite3_free (sql);
*ppVTab = (sqlite3_vtab *) p_vt;
return SQLITE_OK;
}
freexl_select_active_worksheet (handle, worksheet);
freexl_worksheet_dimensions (handle, &rows, &columns);
p_vt->XL_handle = handle;
p_vt->rows = rows;
p_vt->columns = columns;
gaiaOutBufferInitialize (&sql_statement);
xname = gaiaDoubleQuotedSql (argv[2]);
sql = sqlite3_mprintf ("CREATE TABLE \"%s\" (row_no INTEGER", xname);
free (xname);
gaiaAppendToOutBuffer (&sql_statement, sql);
sqlite3_free (sql);
if (firstLineTitles == 'Y')
{
for (col = 0; col < columns; col++)
{
FreeXL_CellValue cell;
int ret = freexl_get_cell_value (handle, 0, col, &cell);
if (ret != FREEXL_OK)
sql = sqlite3_mprintf ("col_%d", col);
else
{
if (cell.type == FREEXL_CELL_INT)
sql = sqlite3_mprintf ("%d", cell.value.int_value);
else if (cell.type == FREEXL_CELL_DOUBLE)
sql =
sqlite3_mprintf ("%1.2f",
cell.value.double_value);
else if (cell.type == FREEXL_CELL_TEXT
|| cell.type == FREEXL_CELL_SST_TEXT
|| cell.type == FREEXL_CELL_DATE
|| cell.type == FREEXL_CELL_DATETIME
|| cell.type == FREEXL_CELL_TIME)
{
int len = strlen (cell.value.text_value);
if (len < 256)
sql =
sqlite3_mprintf ("%s",
cell.value.text_value);
else
sql = sqlite3_mprintf ("col_%d", col);
}
else
sql = sqlite3_mprintf ("col_%d", col);
}
xname = gaiaDoubleQuotedSql (sql);
sqlite3_free (sql);
sql = sqlite3_mprintf (", \"%s\"", xname);
free (xname);
gaiaAppendToOutBuffer (&sql_statement, sql);
sqlite3_free (sql);
}
}
else
{
for (col = 0; col < columns; col++)
{
sql = sqlite3_mprintf ("col_%d", col);
xname = gaiaDoubleQuotedSql (sql);
sqlite3_free (sql);
sql = sqlite3_mprintf (", \"%s\"", xname);
free (xname);
gaiaAppendToOutBuffer (&sql_statement, sql);
sqlite3_free (sql);
}
}
gaiaAppendToOutBuffer (&sql_statement, ")");
if (sql_statement.Error == 0 && sql_statement.Buffer != NULL)
{
if (sqlite3_declare_vtab (db, sql_statement.Buffer) != SQLITE_OK)
{
*pzErr =
sqlite3_mprintf
("[VirtualXL module] CREATE VIRTUAL: invalid SQL statement \"%s\"",
sql_statement.Buffer);
gaiaOutBufferReset (&sql_statement);
return SQLITE_ERROR;
}
}
gaiaOutBufferReset (&sql_statement);
*ppVTab = (sqlite3_vtab *) p_vt;
return SQLITE_OK;
}
static int
vXL_connect (sqlite3 * db, void *pAux, int argc, const char *const *argv,
sqlite3_vtab ** ppVTab, char **pzErr)
{
return vXL_create (db, pAux, argc, argv, ppVTab, pzErr);
}
static int
vXL_best_index (sqlite3_vtab * pVTab, sqlite3_index_info * pIndex)
{
int i;
int iArg = 0;
char str[2048];
char buf[64];
if (pVTab)
pVTab = pVTab;
*str = '\0';
for (i = 0; i < pIndex->nConstraint; i++)
{
if (pIndex->aConstraint[i].usable)
{
iArg++;
pIndex->aConstraintUsage[i].argvIndex = iArg;
pIndex->aConstraintUsage[i].omit = 1;
sprintf (buf, "%d:%d,", pIndex->aConstraint[i].iColumn,
pIndex->aConstraint[i].op);
strcat (str, buf);
}
}
if (*str != '\0')
{
pIndex->idxStr = sqlite3_mprintf ("%s", str);
pIndex->needToFreeIdxStr = 1;
}
return SQLITE_OK;
}
static int
vXL_disconnect (sqlite3_vtab * pVTab)
{
VirtualXLPtr p_vt = (VirtualXLPtr) pVTab;
if (p_vt->XL_handle)
freexl_close (p_vt->XL_handle);
sqlite3_free (p_vt);
return SQLITE_OK;
}
static int
vXL_destroy (sqlite3_vtab * pVTab)
{
return vXL_disconnect (pVTab);
}
static void
vXL_read_row (VirtualXLCursorPtr cursor)
{
cursor->current_row++;
if (cursor->current_row > cursor->pVtab->rows)
{
cursor->eof = 1;
return;
}
}
static int
vXL_open (sqlite3_vtab * pVTab, sqlite3_vtab_cursor ** ppCursor)
{
VirtualXLCursorPtr cursor =
(VirtualXLCursorPtr) sqlite3_malloc (sizeof (VirtualXLCursor));
if (cursor == NULL)
return SQLITE_ERROR;
cursor->firstConstraint = NULL;
cursor->lastConstraint = NULL;
cursor->pVtab = (VirtualXLPtr) pVTab;
if (cursor->pVtab->firstLineTitles == 'Y')
cursor->current_row = 1;
else
cursor->current_row = 0;
cursor->eof = 0;
*ppCursor = (sqlite3_vtab_cursor *) cursor;
vXL_read_row (cursor);
return SQLITE_OK;
}
static void
vXL_free_constraints (VirtualXLCursorPtr cursor)
{
VirtualXLConstraintPtr pC;
VirtualXLConstraintPtr pCn;
pC = cursor->firstConstraint;
while (pC)
{
pCn = pC->next;
if (pC->txtValue)
sqlite3_free (pC->txtValue);
sqlite3_free (pC);
pC = pCn;
}
cursor->firstConstraint = NULL;
cursor->lastConstraint = NULL;
}
static int
vXL_close (sqlite3_vtab_cursor * pCursor)
{
VirtualXLCursorPtr cursor = (VirtualXLCursorPtr) pCursor;
vXL_free_constraints (cursor);
sqlite3_free (pCursor);
return SQLITE_OK;
}
static int
vXL_parse_constraint (const char *str, int index, int *iColumn, int *op)
{
char buf[64];
const char *in = str;
char *out = buf;
int i = 0;
int found = 0;
*out = '\0';
while (*in != '\0')
{
if (*in == ',')
{
if (index == i)
{
*out = '\0';
found = 1;
break;
}
i++;
in++;
continue;
}
if (index == i)
*out++ = *in;
in++;
}
if (!found)
return 0;
in = buf;
for (i = 0; i < (int) strlen (buf); i++)
{
if (buf[i] == ':')
{
buf[i] = '\0';
*iColumn = atoi (buf);
*op = atoi (buf + i + 1);
return 1;
}
in++;
}
return 0;
}
static int
vXL_eval_constraints (VirtualXLCursorPtr cursor)
{
FreeXL_CellValue cell;
VirtualXLConstraintPtr pC = cursor->firstConstraint;
if (pC == NULL)
return 1;
while (pC)
{
int ok = 0;
if (pC->iColumn == 0)
{
if (pC->valueType == 'I')
{
int cur_row = cursor->current_row;
if (cursor->pVtab->firstLineTitles == 'Y')
cur_row--;
switch (pC->op)
{
case SQLITE_INDEX_CONSTRAINT_EQ:
if (cur_row == pC->intValue)
ok = 1;
break;
case SQLITE_INDEX_CONSTRAINT_GT:
if (cur_row > pC->intValue)
ok = 1;
break;
case SQLITE_INDEX_CONSTRAINT_LE:
if (cur_row <= pC->intValue)
ok = 1;
break;
case SQLITE_INDEX_CONSTRAINT_LT:
if (cur_row < pC->intValue)
ok = 1;
break;
case SQLITE_INDEX_CONSTRAINT_GE:
if (cur_row >= pC->intValue)
ok = 1;
break;
};
}
goto done;
}
if (cursor->pVtab->XL_handle != NULL
&& cursor->current_row <= cursor->pVtab->rows
&& pC->iColumn <= cursor->pVtab->columns)
freexl_get_cell_value (cursor->pVtab->XL_handle,
cursor->current_row - 1, pC->iColumn - 1,
&cell);
else
cell.type = FREEXL_CELL_NULL;
if (cell.type == FREEXL_CELL_INT)
{
if (pC->valueType == 'I')
{
switch (pC->op)
{
case SQLITE_INDEX_CONSTRAINT_EQ:
if (cell.value.int_value == pC->intValue)
ok = 1;
break;
case SQLITE_INDEX_CONSTRAINT_GT:
if (cell.value.int_value > pC->intValue)
ok = 1;
break;
case SQLITE_INDEX_CONSTRAINT_LE:
if (cell.value.int_value <= pC->intValue)
ok = 1;
break;
case SQLITE_INDEX_CONSTRAINT_LT:
if (cell.value.int_value < pC->intValue)
ok = 1;
break;
case SQLITE_INDEX_CONSTRAINT_GE:
if (cell.value.int_value >= pC->intValue)
ok = 1;
break;
};
}
if (pC->valueType == 'D')
{
switch (pC->op)
{
case SQLITE_INDEX_CONSTRAINT_EQ:
if (cell.value.int_value == pC->dblValue)
ok = 1;
break;
case SQLITE_INDEX_CONSTRAINT_GT:
if (cell.value.int_value > pC->dblValue)
ok = 1;
break;
case SQLITE_INDEX_CONSTRAINT_LE:
if (cell.value.int_value <= pC->dblValue)
ok = 1;
break;
case SQLITE_INDEX_CONSTRAINT_LT:
if (cell.value.int_value < pC->dblValue)
ok = 1;
break;
case SQLITE_INDEX_CONSTRAINT_GE:
if (cell.value.int_value >= pC->dblValue)
ok = 1;
break;
};
}
}
if (cell.type == FREEXL_CELL_DOUBLE)
{
if (pC->valueType == 'I')
{
switch (pC->op)
{
case SQLITE_INDEX_CONSTRAINT_EQ:
if (cell.value.double_value == pC->intValue)
ok = 1;
break;
case SQLITE_INDEX_CONSTRAINT_GT:
if (cell.value.double_value > pC->intValue)
ok = 1;
break;
case SQLITE_INDEX_CONSTRAINT_LE:
if (cell.value.double_value <= pC->intValue)
ok = 1;
break;
case SQLITE_INDEX_CONSTRAINT_LT:
if (cell.value.double_value < pC->intValue)
ok = 1;
break;
case SQLITE_INDEX_CONSTRAINT_GE:
if (cell.value.double_value >= pC->intValue)
ok = 1;
break;
};
}
if (pC->valueType == 'D')
{
switch (pC->op)
{
case SQLITE_INDEX_CONSTRAINT_EQ:
if (cell.value.double_value == pC->dblValue)
ok = 1;
break;
case SQLITE_INDEX_CONSTRAINT_GT:
if (cell.value.double_value > pC->dblValue)
ok = 1;
break;
case SQLITE_INDEX_CONSTRAINT_LE:
if (cell.value.double_value <= pC->dblValue)
ok = 1;
break;
case SQLITE_INDEX_CONSTRAINT_LT:
if (cell.value.double_value < pC->dblValue)
ok = 1;
break;
case SQLITE_INDEX_CONSTRAINT_GE:
if (cell.value.double_value >= pC->dblValue)
ok = 1;
break;
};
}
}
if ((cell.type == FREEXL_CELL_TEXT
|| cell.type == FREEXL_CELL_SST_TEXT
|| cell.type == FREEXL_CELL_DATE
|| cell.type == FREEXL_CELL_DATETIME
|| cell.type == FREEXL_CELL_TIME) && pC->valueType == 'T')
{
int ret = strcmp (cell.value.text_value, pC->txtValue);
switch (pC->op)
{
case SQLITE_INDEX_CONSTRAINT_EQ:
if (ret == 0)
ok = 1;
break;
case SQLITE_INDEX_CONSTRAINT_GT:
if (ret > 0)
ok = 1;
break;
case SQLITE_INDEX_CONSTRAINT_LE:
if (ret <= 0)
ok = 1;
break;
case SQLITE_INDEX_CONSTRAINT_LT:
if (ret < 0)
ok = 1;
break;
case SQLITE_INDEX_CONSTRAINT_GE:
if (ret >= 0)
ok = 1;
break;
};
}
done:
if (!ok)
return 0;
pC = pC->next;
}
return 1;
}
static int
vXL_filter (sqlite3_vtab_cursor * pCursor, int idxNum, const char *idxStr,
int argc, sqlite3_value ** argv)
{
int i;
int iColumn;
int op;
int len;
VirtualXLConstraintPtr pC;
VirtualXLCursorPtr cursor = (VirtualXLCursorPtr) pCursor;
if (idxNum)
idxNum = idxNum;
vXL_free_constraints (cursor);
for (i = 0; i < argc; i++)
{
if (!vXL_parse_constraint (idxStr, i, &iColumn, &op))
continue;
pC = sqlite3_malloc (sizeof (VirtualXLConstraint));
if (!pC)
continue;
pC->iColumn = iColumn;
pC->op = op;
pC->valueType = '\0';
pC->txtValue = NULL;
pC->next = NULL;
if (sqlite3_value_type (argv[i]) == SQLITE_INTEGER)
{
pC->valueType = 'I';
pC->intValue = sqlite3_value_int64 (argv[i]);
}
if (sqlite3_value_type (argv[i]) == SQLITE_FLOAT)
{
pC->valueType = 'D';
pC->dblValue = sqlite3_value_double (argv[i]);
}
if (sqlite3_value_type (argv[i]) == SQLITE_TEXT)
{
pC->valueType = 'T';
len = sqlite3_value_bytes (argv[i]) + 1;
pC->txtValue = (char *) sqlite3_malloc (len);
if (pC->txtValue)
strcpy (pC->txtValue,
(char *) sqlite3_value_text (argv[i]));
}
if (cursor->firstConstraint == NULL)
cursor->firstConstraint = pC;
if (cursor->lastConstraint != NULL)
cursor->lastConstraint->next = pC;
cursor->lastConstraint = pC;
}
if (cursor->pVtab->firstLineTitles == 'Y')
cursor->current_row = 1;
else
cursor->current_row = 0;
cursor->eof = 0;
while (1)
{
vXL_read_row (cursor);
if (cursor->eof)
break;
if (vXL_eval_constraints (cursor))
break;
}
return SQLITE_OK;
}
static int
vXL_next (sqlite3_vtab_cursor * pCursor)
{
VirtualXLCursorPtr cursor = (VirtualXLCursorPtr) pCursor;
while (1)
{
vXL_read_row (cursor);
if (cursor->eof)
break;
if (vXL_eval_constraints (cursor))
break;
}
return SQLITE_OK;
}
static int
vXL_eof (sqlite3_vtab_cursor * pCursor)
{
VirtualXLCursorPtr cursor = (VirtualXLCursorPtr) pCursor;
return cursor->eof;
}
static int
vXL_column (sqlite3_vtab_cursor * pCursor, sqlite3_context * pContext,
int column)
{
FreeXL_CellValue cell;
VirtualXLCursorPtr cursor = (VirtualXLCursorPtr) pCursor;
if (column == 0)
{
if (cursor->pVtab->firstLineTitles == 'Y')
sqlite3_result_int (pContext, cursor->current_row - 1);
else
sqlite3_result_int (pContext, cursor->current_row);
return SQLITE_OK;
}
if (cursor->pVtab->XL_handle != NULL
&& cursor->current_row <= cursor->pVtab->rows
&& column <= cursor->pVtab->columns)
freexl_get_cell_value (cursor->pVtab->XL_handle,
cursor->current_row - 1, column - 1, &cell);
else
cell.type = FREEXL_CELL_NULL;
switch (cell.type)
{
case FREEXL_CELL_INT:
sqlite3_result_int (pContext, cell.value.int_value);
break;
case FREEXL_CELL_DOUBLE:
sqlite3_result_double (pContext, cell.value.double_value);
break;
case FREEXL_CELL_TEXT:
case FREEXL_CELL_SST_TEXT:
case FREEXL_CELL_DATE:
case FREEXL_CELL_DATETIME:
case FREEXL_CELL_TIME:
sqlite3_result_text (pContext, cell.value.text_value,
strlen (cell.value.text_value), SQLITE_STATIC);
break;
default:
sqlite3_result_null (pContext);
break;
};
return SQLITE_OK;
}
static int
vXL_rowid (sqlite3_vtab_cursor * pCursor, sqlite_int64 * pRowid)
{
VirtualXLCursorPtr cursor = (VirtualXLCursorPtr) pCursor;
if (cursor->pVtab->firstLineTitles == 'Y')
*pRowid = cursor->current_row - 1;
else
*pRowid = cursor->current_row;
return SQLITE_OK;
}
static int
vXL_update (sqlite3_vtab * pVTab, int argc, sqlite3_value ** argv,
sqlite_int64 * pRowid)
{
if (pVTab || argc || argv || pRowid)
pVTab = pVTab;
return SQLITE_READONLY;
}
static int
vXL_begin (sqlite3_vtab * pVTab)
{
if (pVTab)
pVTab = pVTab;
return SQLITE_OK;
}
static int
vXL_sync (sqlite3_vtab * pVTab)
{
if (pVTab)
pVTab = pVTab;
return SQLITE_OK;
}
static int
vXL_commit (sqlite3_vtab * pVTab)
{
if (pVTab)
pVTab = pVTab;
return SQLITE_OK;
}
static int
vXL_rollback (sqlite3_vtab * pVTab)
{
if (pVTab)
pVTab = pVTab;
return SQLITE_OK;
}
static int
vXL_rename (sqlite3_vtab * pVTab, const char *zNew)
{
if (pVTab)
pVTab = pVTab;
if (zNew)
zNew = zNew;
return SQLITE_ERROR;
}
static int
spliteVirtualXLInit (sqlite3 * db)
{
int rc = SQLITE_OK;
my_XL_module.iVersion = 1;
my_XL_module.xCreate = &vXL_create;
my_XL_module.xConnect = &vXL_connect;
my_XL_module.xBestIndex = &vXL_best_index;
my_XL_module.xDisconnect = &vXL_disconnect;
my_XL_module.xDestroy = &vXL_destroy;
my_XL_module.xOpen = &vXL_open;
my_XL_module.xClose = &vXL_close;
my_XL_module.xFilter = &vXL_filter;
my_XL_module.xNext = &vXL_next;
my_XL_module.xEof = &vXL_eof;
my_XL_module.xColumn = &vXL_column;
my_XL_module.xRowid = &vXL_rowid;
my_XL_module.xUpdate = &vXL_update;
my_XL_module.xBegin = &vXL_begin;
my_XL_module.xSync = &vXL_sync;
my_XL_module.xCommit = &vXL_commit;
my_XL_module.xRollback = &vXL_rollback;
my_XL_module.xFindFunction = NULL;
my_XL_module.xRename = &vXL_rename;
sqlite3_create_module_v2 (db, "VirtualXL", &my_XL_module, NULL, 0);
return rc;
}
SPATIALITE_PRIVATE int
virtualXL_extension_init (void *xdb)
{
sqlite3 *db = (sqlite3 *) xdb;
return spliteVirtualXLInit (db);
}
#endif
#endif