#include <stdio.h> /* ** Callback for executed SQL script. Print to stdout. */ static int csExecCb( void *pCtx, int nStr, char **azStr, char **azCol ){ if( azStr ){ int ii; const char *zSep = ""; for(ii=0; ii<nStr; ii++){ fprintf(stdout, "%s%s", zSep, azStr[ii] ? azStr[ii] : "(null)"); zSep = "|"; } fprintf(stdout, "\n"); } return 0; } #include "blockcachevfs.h" #include "sqlite3.h" /* ** This program is hardcoded to access an Azure emulator running on port ** 10000 (the default for Azurite) of the localhost. It is also hardcoded ** to the demo account - the account built-in to the emulator with the ** well-known credentials reproduced below. */ #define CS_STORAGE "azure?emulator=127.0.0.1:10000" #define CS_ACCOUNT "devstoreaccount1" #define CS_KEY "Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==" /* ** Authentication callback. A real application would return a different ** authentication token based on the storage system, account name and ** container name parameters, but since the credentials used by this ** application are hard coded, it just returns a copy of constant string ** CS_KEY. ** ** Because the API is defined such that this function must return a buffer ** allocated using sqlite3_malloc() or compatible, this implementation ** uses sqlite3_mprintf() to make a copy of the authentication token. */ static int csAuthCb( void *pCtx, const char *zStorage, const char *zAccount, const char *zContainer, char **pzAuthToken ){ *pzAuthToken = sqlite3_mprintf("%s", CS_KEY); return (*pzAuthToken) ? SQLITE_OK : SQLITE_NOMEM; } /* ** Open a VFS that uses directory zDir as its cache directory. Then attach ** container zCont. Next, open an SQLite handle on path zPath using the new ** VFS and execute SQL script zSql. */ static int cloudsql( const char *zDir, /* Directory to use for blockcachevfs cache */ const char *zCont, /* Container to attach */ const char *zPath, /* Path to open */ const char *zSql /* SQL to execute */ ){ int rc = SQLITE_OK; /* Error code */ char *zErr = 0; /* Error message */ sqlite3_bcvfs *pVfs = 0; /* VFS handle */ sqlite3 *db = 0; /* Database handle open on zPath */ /* Create a VFS object. Directory zDir must already exist. If it exists ** and there is a daemon running in that directory, the new VFS connects ** to the daemon for read-only access. Or, if there is no such daemon, ** the new VFS will provide read-write daemonless access. */ rc = sqlite3_bcvfs_create(zDir, "myvfs", &pVfs, &zErr); /* Check if this is a daemon VFS or not */ if( rc==SQLITE_OK ){ if( sqlite3_bcvfs_isdaemon(pVfs) ){ printf("VFS is using a daemon\n"); }else{ printf("VFS is in daemonless mode\n"); } } /* Configure the authorization callback. */ if( rc==SQLITE_OK ){ sqlite3_bcvfs_auth_callback(pVfs, 0, csAuthCb); } /* Attach the container. Specify the SQLITE_BCV_ATTACH_IFNOT flag so that ** it is not an error if the container is already attached. ** ** There are two reasons the container might already be attached, even ** though the VFS was only just created. Firstly, if this VFS is connected ** to a running daemon process, then some other client may have already ** attached the container to the daemon. Secondly, VFS objects store their ** state in the cache directory so that if they are restarted, all ** containers are automatically reattached. So if this (or some other ** blockcachevfs application) has run before specifying the same ** cache directory, the container may already be attached. */ if( rc==SQLITE_OK ){ rc = sqlite3_bcvfs_attach(pVfs, CS_STORAGE, CS_ACCOUNT, zCont, 0, SQLITE_BCV_ATTACH_IFNOT, &zErr ); } /* Open a database handle on a cloud database. */ if( rc==SQLITE_OK ){ rc = sqlite3_open_v2(zPath, &db, SQLITE_OPEN_READWRITE, "myvfs"); if( rc!=SQLITE_OK ){ zErr = sqlite3_mprintf("%s", sqlite3_errmsg(db)); } } /* Enable the virtual table interface. */ if( rc==SQLITE_OK ){ rc = sqlite3_bcvfs_register_vtab(db); } /* Execute the provided SQL script. */ if( rc==SQLITE_OK ){ rc = sqlite3_exec(db, zSql, csExecCb, 0, &zErr); } sqlite3_close(db); sqlite3_bcvfs_destroy(pVfs); /* Output any error, free any error message and return. */ if( rc!=SQLITE_OK ){ fprintf(stderr, "Error: (%d) %s\n", rc, zErr); } sqlite3_free(zErr); return rc; } /* ** Usage: cloudsql DIRECTORY CONTAINER DBPATH SQL */ int main(int argc, char **argv){ if( argc!=5 ){ fprintf(stderr, "Usage: %s DIRECTORY CONTAINER DBPATH SQL\n", argv[0]); return -1; } return cloudsql(argv[1], argv[2], argv[3], argv[4]); }