diff --git a/Makefile b/Makefile index 93a8e06..3bfc024 100644 --- a/Makefile +++ b/Makefile @@ -20,15 +20,13 @@ WIN_LDFLAGS=-static -lws2_32 -lwsock32 #-g3 -fsanitize=address WIN_SERVER=bin/winfusion.exe CSRC=\ + src/contrib/sqlite/sqlite3.c\ src/contrib/bcrypt/bcrypt.c\ src/contrib/bcrypt/crypt_blowfish.c\ src/contrib/bcrypt/crypt_gensalt.c\ src/contrib/bcrypt/wrapper.c\ - src/contrib/sqlite/sqlite3.c\ CXXSRC=\ - src/contrib/sqlite/sqlite3pp.cpp\ - src/contrib/sqlite/sqlite3ppext.cpp\ src/ChatManager.cpp\ src/CombatManager.cpp\ src/CNLoginServer.cpp\ @@ -50,18 +48,16 @@ CXXSRC=\ # headers (for timestamp purposes) CHDR=\ + src/contrib/sqlite/sqlite3.h\ + src/contrib/sqlite/sqlite_orm.h\ src/contrib/bcrypt/bcrypt.h\ src/contrib/bcrypt/crypt_blowfish.h\ src/contrib/bcrypt/crypt_gensalt.h\ src/contrib/bcrypt/ow-crypt.h\ src/contrib/bcrypt/winbcrypt.h\ - src/contrib/sqlite/sqlite3.h\ - src/contrib/sqlite/sqlite3ext.h\ CXXHDR=\ src/contrib/bcrypt/BCrypt.hpp\ - src/contrib/sqlite/sqlite3pp.h\ - src/contrib/sqlite/sqlite3ppext.h\ src/contrib/INIReader.hpp\ src/contrib/JSON.hpp\ src/ChatManager.hpp\ diff --git a/config.ini b/config.ini index 56e7550..c179144 100644 --- a/config.ini +++ b/config.ini @@ -12,6 +12,8 @@ port=8001 # enables two randomly generated characters in the # character selection menu for convenience randomcharacters=true +# will all custom names be approved instantly? +acceptallcustomnames=true # Shard Server configuration [shard] @@ -30,7 +32,7 @@ warpdata=data/warps.json gm=true # spawn coordinates (Z is height) -# the supplied defaults are at City Hall +# the supplied defaults are at Sector V (future) spawnx=632032 spawny=187177 spawnz=-5500 diff --git a/src/CNLoginServer.cpp b/src/CNLoginServer.cpp index 9e5f5fb..da3233a 100644 --- a/src/CNLoginServer.cpp +++ b/src/CNLoginServer.cpp @@ -1,9 +1,12 @@ #include "CNLoginServer.hpp" #include "CNShared.hpp" #include "CNStructs.hpp" +#include "Database.hpp" +#include "PlayerManager.hpp" +#include +#include "contrib/bcrypt/BCrypt.hpp" #include "settings.hpp" -#include "Database.hpp" std::map CNLoginServer::loginSessions; @@ -17,61 +20,85 @@ void CNLoginServer::handlePacket(CNSocket* sock, CNPacketData* data) { printPacket(data, CL2LS); switch (data->type) { - case P_CL2LS_REQ_LOGIN: { - if (data->size != sizeof(sP_CL2LS_REQ_LOGIN)) - return; // ignore the malformed packet + case P_CL2LS_REQ_LOGIN: { + if (data->size != sizeof(sP_CL2LS_REQ_LOGIN)) + return; // ignore the malformed packet - sP_CL2LS_REQ_LOGIN* login = (sP_CL2LS_REQ_LOGIN*)data->buf; - bool success = false; - int errorCode = 0; - std::string userLogin = U16toU8(login->szID); - std::string userPassword = U16toU8(login->szPassword); + sP_CL2LS_REQ_LOGIN* login = (sP_CL2LS_REQ_LOGIN*)data->buf; + //TODO: implement better way of sending credentials + std::string userLogin = U16toU8(login->szID); + std::string userPassword = U16toU8(login->szPassword); - if (!Database::isLoginDataGood(userLogin, userPassword)) { - // failed regex - errorCode = (int)LOGINERRORID::login_error; - } else if (!Database::doesUserExist(U16toU8(login->szID))) { - // if user does not exist in db, add him to and send succ - Database::addAccount(U16toU8(login->szID), U16toU8(login->szPassword)); - success = true; - } else if (Database::isPasswordCorrect((U16toU8(login->szID)), U16toU8(login->szPassword))) { - // if user exists, check if password is correct - success = true; - } else { - errorCode = (int)LOGINERRORID::id_and_password_do_not_match; - } + bool success = false; + int errorCode = 0; - if (success) { - int userID = Database::getUserID(userLogin); - int charCount = Database::getUserSlotsNum(userID); + //checking regex + if (!CNLoginServer::isLoginDataGood(userLogin, userPassword)) + { + errorCode = (int)LOGINERRORID::login_error; + } + else + { + std::unique_ptr findUser = Database::findAccount(userLogin); + //if account not found, create it + if (findUser == nullptr) + { + loginSessions[sock] = CNLoginData(); + loginSessions[sock].userID = Database::addAccount(userLogin, userPassword); + loginSessions[sock].slot = 1; + success = true; + } + //if user exists, check if password is correct + else if (CNLoginServer::isPasswordCorrect(findUser->Password, userPassword)) + { + //check if account isn't currently in use + if (CNLoginServer::isAccountInUse(findUser->AccountID) || + PlayerManager::isAccountInUse(findUser->AccountID)) + { + errorCode = (int)LOGINERRORID::id_already_in_use; + } + //if not, login success + else + { + loginSessions[sock] = CNLoginData(); + loginSessions[sock].userID = findUser->AccountID; + loginSessions[sock].slot = findUser->Selected; + success = true; + } + } + else + { + errorCode = (int)LOGINERRORID::id_and_password_do_not_match; + } + } - INITSTRUCT(sP_LS2CL_REP_LOGIN_SUCC, resp); - // set username in resp packet - memcpy(resp.szID, login->szID, sizeof(char16_t) * 33); - resp.iCharCount = charCount; - resp.iSlotNum = 1; - resp.iPaymentFlag = 1; - resp.iOpenBetaFlag = 0; - resp.uiSvrTime = getTime(); + if (success) + { + std::vector characters = Database::getCharacters(loginSessions[sock].userID); + int charCount = characters.size(); - // send the resp in with original key - sock->sendPacket((void*)&resp, P_LS2CL_REP_LOGIN_SUCC, sizeof(sP_LS2CL_REP_LOGIN_SUCC)); + INITSTRUCT(sP_LS2CL_REP_LOGIN_SUCC, resp); + // set username in resp packet + memcpy(resp.szID, login->szID, sizeof(char16_t) * 33); - // update keys - sock->setEKey(CNSocketEncryption::createNewKey(resp.uiSvrTime, resp.iCharCount + 1, resp.iSlotNum + 1)); - sock->setFEKey(CNSocketEncryption::createNewKey((uint64_t)(*(uint64_t*)&CNSocketEncryption::defaultKey[0]), login->iClientVerC, 1)); + resp.iCharCount = charCount; + resp.iSlotNum = loginSessions[sock].slot; + resp.iPaymentFlag = 1; + resp.iOpenBetaFlag = 0; + resp.uiSvrTime = getTime(); + + // send the resp in with original key + sock->sendPacket((void*)&resp, P_LS2CL_REP_LOGIN_SUCC, sizeof(sP_LS2CL_REP_LOGIN_SUCC)); - loginSessions[sock] = CNLoginData(); - loginSessions[sock].userID = userID; - - // now send the characters :) - - if (charCount > 0) { - - std::list characters = Database::getCharacters(loginSessions[sock].userID); - std::list::iterator it; - for (it = characters.begin(); it != characters.end(); it++) { + // update keys + sock->setEKey(CNSocketEncryption::createNewKey(resp.uiSvrTime, resp.iCharCount + 1, resp.iSlotNum + 1)); + sock->setFEKey(CNSocketEncryption::createNewKey((uint64_t)(*(uint64_t*)&CNSocketEncryption::defaultKey[0]), login->iClientVerC, 1)); + + // now send the characters :) + std::vector::iterator it; + for (it = characters.begin(); it != characters.end(); it++) + { sP_LS2CL_REP_CHAR_INFO charInfo = sP_LS2CL_REP_CHAR_INFO(); charInfo.iSlot = (int8_t)it->slot; @@ -85,23 +112,14 @@ void CNLoginServer::handlePacket(CNSocket* sock, CNPacketData* data) { charInfo.iZ = it->z; //save character in session (for char select) - int64_t UID = charInfo.sPC_Style.iPC_UID; - loginSessions[sock].characters[UID] = Player(); - loginSessions[sock].characters[UID].level = charInfo.iLevel; - loginSessions[sock].characters[UID].slot = charInfo.iSlot; + int UID = it->iID; + loginSessions[sock].characters[UID] = Player(*it); loginSessions[sock].characters[UID].FEKey = sock->getFEKey(); - loginSessions[sock].characters[UID].x = charInfo.iX; - loginSessions[sock].characters[UID].y = charInfo.iY; - loginSessions[sock].characters[UID].z = charInfo.iZ; - loginSessions[sock].characters[UID].PCStyle = charInfo.sPC_Style; - loginSessions[sock].characters[UID].PCStyle2 = charInfo.sPC_Style2; - loginSessions[sock].characters[UID].IsGM = false; - loginSessions[sock].characters[UID].money = 9001; + //temporary inventory stuff for (int i = 0; i < 4; i++) { //equip char creation clothes and lightning rifle charInfo.aEquip[i] = it->Equip[i]; - loginSessions[sock].characters[UID].Equip[i] = charInfo.aEquip[i]; } for (int i = 5; i < AEQUIP_COUNT; i++) { @@ -109,244 +127,237 @@ void CNLoginServer::handlePacket(CNSocket* sock, CNPacketData* data) { charInfo.aEquip[i].iID = 0; charInfo.aEquip[i].iType = i; charInfo.aEquip[i].iOpt = 0; - loginSessions[sock].characters[UID].Equip[i] = charInfo.aEquip[i]; - } - - - for (int i = 0; i < AINVEN_COUNT; i++) { - // setup inventories - loginSessions[sock].characters[UID].Inven[i].iID = 0; - loginSessions[sock].characters[UID].Inven[i].iType = 0; - loginSessions[sock].characters[UID].Inven[i].iOpt = 0; } // set default to the first character if (it == characters.begin()) loginSessions[sock].selectedChar = UID; - sock->sendPacket((void*)&charInfo, P_LS2CL_REP_CHAR_INFO, sizeof(sP_LS2CL_REP_CHAR_INFO)); + sock->sendPacket((void*)&charInfo, P_LS2CL_REP_CHAR_INFO, sizeof(sP_LS2CL_REP_CHAR_INFO)); } + } + //Failure + else { + INITSTRUCT(sP_LS2CL_REP_LOGIN_FAIL, resp); + + memcpy(resp.szID, login->szID, sizeof(char16_t) * 33); + resp.iErrorCode = errorCode; + + sock->sendPacket((void*)&resp, P_LS2CL_REP_LOGIN_FAIL, sizeof(sP_LS2CL_REP_LOGIN_FAIL)); } - } else { - // failed - INITSTRUCT(sP_LS2CL_REP_LOGIN_FAIL, resp); - memcpy(resp.szID, login->szID, sizeof(char16_t) * 33); - resp.iErrorCode = errorCode; - - sock->sendPacket((void*)&resp, P_LS2CL_REP_LOGIN_FAIL, sizeof(sP_LS2CL_REP_LOGIN_FAIL)); + break; } - break; - } - case P_CL2LS_REP_LIVE_CHECK: { - // stubbed, the client really doesn't care LOL - break; - } - case P_CL2LS_REQ_CHECK_CHAR_NAME: { - if (data->size != sizeof(sP_CL2LS_REQ_CHECK_CHAR_NAME)) - return; + case P_CL2LS_REP_LIVE_CHECK: { + // stubbed, the client really doesn't care LOL + break; + } + case P_CL2LS_REQ_CHECK_CHAR_NAME: { + if (data->size != sizeof(sP_CL2LS_REQ_CHECK_CHAR_NAME)) + return; + + // naughty words allowed!!!!!!!! (also for some reason, the client will always show 'Player 0' if you manually type a name. It will show up for other connected players though) + sP_CL2LS_REQ_CHECK_CHAR_NAME* nameCheck = (sP_CL2LS_REQ_CHECK_CHAR_NAME*)data->buf; + //check if name is occupied + if (Database::isNameFree(nameCheck)) + { + // naughty words allowed!!!!!!!! (also for some reason, the client will always show 'Player + ID' if you manually type a name. It will show up for other connected players though) - // naughty words allowed!!!!!!!! (also for some reason, the client will always show 'Player 0' if you manually type a name. It will show up for other connected players though) - sP_CL2LS_REQ_CHECK_CHAR_NAME* nameCheck = (sP_CL2LS_REQ_CHECK_CHAR_NAME*)data->buf; - //check if name is occupied - if (Database::isNameFree(U16toU8(nameCheck->szFirstName), U16toU8(nameCheck->szLastName))) - { + INITSTRUCT(sP_LS2CL_REP_CHECK_CHAR_NAME_SUCC, resp); - // naughty words allowed!!!!!!!! (also for some reason, the client will always show 'Player + ID' if you manually type a name. It will show up for other connected players though) - - INITSTRUCT(sP_LS2CL_REP_CHECK_CHAR_NAME_SUCC, resp); - - DEBUGLOG( + DEBUGLOG( std::cout << "P_CL2LS_REQ_CHECK_CHAR_NAME:" << std::endl; - std::cout << "\tFirstName: " << U16toU8(nameCheck->szFirstName) << " LastName: " << U16toU8(nameCheck->szLastName) << std::endl; - ) + std::cout << "\tFirstName: " << U16toU8(nameCheck->szFirstName) << " LastName: " << U16toU8(nameCheck->szLastName) << std::endl; + ) memcpy(resp.szFirstName, nameCheck->szFirstName, sizeof(char16_t) * 9); - memcpy(resp.szLastName, nameCheck->szLastName, sizeof(char16_t) * 17); + memcpy(resp.szLastName, nameCheck->szLastName, sizeof(char16_t) * 17); + + // fr*ck allowed!!! + sock->sendPacket((void*)&resp, P_LS2CL_REP_CHECK_CHAR_NAME_SUCC, sizeof(sP_LS2CL_REP_CHECK_CHAR_NAME_SUCC)); + } + else { + INITSTRUCT(sP_LS2CL_REP_CHECK_CHAR_NAME_FAIL, resp); + resp.iErrorCode = 1; + sock->sendPacket((void*)&resp, P_LS2CL_REP_CHECK_CHAR_NAME_FAIL, sizeof(sP_LS2CL_REP_CHECK_CHAR_NAME_FAIL)); + } + break; - // fr*ck allowed!!! - sock->sendPacket((void*)&resp, P_LS2CL_REP_CHECK_CHAR_NAME_SUCC, sizeof(sP_LS2CL_REP_CHECK_CHAR_NAME_SUCC)); } - else { - INITSTRUCT(sP_LS2CL_REP_CHECK_CHAR_NAME_FAIL, resp); - resp.iErrorCode = 1; - sock->sendPacket((void*)&resp, P_LS2CL_REP_CHECK_CHAR_NAME_FAIL, sizeof(sP_LS2CL_REP_CHECK_CHAR_NAME_FAIL)); - } - break; - - } - case P_CL2LS_REQ_SAVE_CHAR_NAME: { - if (data->size != sizeof(sP_CL2LS_REQ_SAVE_CHAR_NAME)) - return; - - sP_CL2LS_REQ_SAVE_CHAR_NAME* save = (sP_CL2LS_REQ_SAVE_CHAR_NAME*)data->buf; - Database::createCharacter(save, loginSessions[sock].userID); - - INITSTRUCT(sP_LS2CL_REP_SAVE_CHAR_NAME_SUCC, resp); - - DEBUGLOG( - std::cout << "P_CL2LS_REQ_SAVE_CHAR_NAME:" << std::endl; - std::cout << "\tSlot: " << (int)save->iSlotNum << std::endl; - std::cout << "\tName: " << U16toU8(save->szFirstName) << " " << U16toU8(save->szLastName) << std::endl; - ) + case P_CL2LS_REQ_SAVE_CHAR_NAME: { + if (data->size != sizeof(sP_CL2LS_REQ_SAVE_CHAR_NAME)) + return; + + sP_CL2LS_REQ_SAVE_CHAR_NAME* save = (sP_CL2LS_REQ_SAVE_CHAR_NAME*)data->buf; + INITSTRUCT(sP_LS2CL_REP_SAVE_CHAR_NAME_SUCC, resp); + + DEBUGLOG( + std::cout << "P_CL2LS_REQ_SAVE_CHAR_NAME:" << std::endl; + std::cout << "\tSlot: " << (int)save->iSlotNum << std::endl; + std::cout << "\tName: " << U16toU8(save->szFirstName) << " " << U16toU8(save->szLastName) << std::endl; + ) resp.iSlotNum = save->iSlotNum; - resp.iGender = save->iGender; - resp.iPC_UID = Database::getCharacterID(loginSessions[sock].userID, save->iSlotNum); - memcpy(resp.szFirstName, save->szFirstName, sizeof(char16_t) * 9); - memcpy(resp.szLastName, save->szLastName, sizeof(char16_t) * 17); + resp.iGender = save->iGender; + resp.iPC_UID = Database::createCharacter(save, loginSessions[sock].userID); + memcpy(resp.szFirstName, save->szFirstName, sizeof(char16_t) * 9); + memcpy(resp.szLastName, save->szLastName, sizeof(char16_t) * 17); - sock->sendPacket((void*)&resp, P_LS2CL_REP_SAVE_CHAR_NAME_SUCC, sizeof(sP_LS2CL_REP_SAVE_CHAR_NAME_SUCC)); - break; - } - case P_CL2LS_REQ_CHAR_CREATE: { - if (data->size != sizeof(sP_CL2LS_REQ_CHAR_CREATE)) - return; + sock->sendPacket((void*)&resp, P_LS2CL_REP_SAVE_CHAR_NAME_SUCC, sizeof(sP_LS2CL_REP_SAVE_CHAR_NAME_SUCC)); - sP_CL2LS_REQ_CHAR_CREATE* character = (sP_CL2LS_REQ_CHAR_CREATE*)data->buf; - Database::finishCharacter(character); + Database::updateSelected(loginSessions[sock].userID, save->iSlotNum); + break; + } + case P_CL2LS_REQ_CHAR_CREATE: { + if (data->size != sizeof(sP_CL2LS_REQ_CHAR_CREATE)) + return; + + sP_CL2LS_REQ_CHAR_CREATE* character = (sP_CL2LS_REQ_CHAR_CREATE*)data->buf; + Database::finishCharacter(character); - INITSTRUCT(sP_LS2CL_REP_CHAR_CREATE_SUCC, resp); + DEBUGLOG( + std::cout << "P_CL2LS_REQ_CHAR_CREATE:" << std::endl; + std::cout << "\tPC_UID: " << character->PCStyle.iPC_UID << std::endl; + std::cout << "\tNameCheck: " << (int)character->PCStyle.iNameCheck << std::endl; + std::cout << "\tName: " << U16toU8(character->PCStyle.szFirstName) << " " << U16toU8(character->PCStyle.szLastName) << std::endl; + std::cout << "\tGender: " << (int)character->PCStyle.iGender << std::endl; + std::cout << "\tFace: " << (int)character->PCStyle.iFaceStyle << std::endl; + std::cout << "\tHair: " << (int)character->PCStyle.iHairStyle << std::endl; + std::cout << "\tHair Color: " << (int)character->PCStyle.iHairColor << std::endl; + std::cout << "\tSkin Color: " << (int)character->PCStyle.iSkinColor << std::endl; + std::cout << "\tEye Color: " << (int)character->PCStyle.iEyeColor << std::endl; + std::cout << "\tHeight: " << (int)character->PCStyle.iHeight << std::endl; + std::cout << "\tBody: " << (int)character->PCStyle.iBody << std::endl; + std::cout << "\tClass: " << (int)character->PCStyle.iClass << std::endl; + std::cout << "\tiEquipUBID: " << (int)character->sOn_Item.iEquipUBID << std::endl; + std::cout << "\tiEquipLBID: " << (int)character->sOn_Item.iEquipLBID << std::endl; + std::cout << "\tiEquipFootID: " << (int)character->sOn_Item.iEquipFootID << std::endl; + ) + + Player player = + Database::DbToPlayer( + Database::getDbPlayerById(character->PCStyle.iPC_UID) + ); + int64_t UID = player.iID; - DEBUGLOG( - std::cout << "P_CL2LS_REQ_CHAR_CREATE:" << std::endl; - std::cout << "\tPC_UID: " << character->PCStyle.iPC_UID << std::endl; - std::cout << "\tNameCheck: " << (int)character->PCStyle.iNameCheck << std::endl; - std::cout << "\tName: " << U16toU8(character->PCStyle.szFirstName) << " " << U16toU8(character->PCStyle.szLastName) << std::endl; - std::cout << "\tGender: " << (int)character->PCStyle.iGender << std::endl; - std::cout << "\tFace: " << (int)character->PCStyle.iFaceStyle << std::endl; - std::cout << "\tHair: " << (int)character->PCStyle.iHairStyle << std::endl; - std::cout << "\tHair Color: " << (int)character->PCStyle.iHairColor << std::endl; - std::cout << "\tSkin Color: " << (int)character->PCStyle.iSkinColor << std::endl; - std::cout << "\tEye Color: " << (int)character->PCStyle.iEyeColor << std::endl; - std::cout << "\tHeight: " << (int)character->PCStyle.iHeight << std::endl; - std::cout << "\tBody: " << (int)character->PCStyle.iBody << std::endl; - std::cout << "\tClass: " << (int)character->PCStyle.iClass << std::endl; - std::cout << "\tiEquipUBID: " << (int)character->sOn_Item.iEquipUBID << std::endl; - std::cout << "\tiEquipLBID: " << (int)character->sOn_Item.iEquipLBID << std::endl; - std::cout << "\tiEquipFootID: " << (int)character->sOn_Item.iEquipFootID << std::endl; - ) + INITSTRUCT(sP_LS2CL_REP_CHAR_CREATE_SUCC, resp); + resp.sPC_Style = player.PCStyle; + resp.sPC_Style2 = player.PCStyle2; + resp.iLevel = player.level; + resp.sOn_Item = character->sOn_Item; - int64_t UID = character->PCStyle.iPC_UID; + //save player in session + loginSessions[sock].characters[UID] = Player(player); + loginSessions[sock].characters[UID].FEKey = sock->getFEKey(); + + sock->sendPacket((void*)&resp, P_LS2CL_REP_CHAR_CREATE_SUCC, sizeof(sP_LS2CL_REP_CHAR_CREATE_SUCC)); + Database::updateSelected(loginSessions[sock].userID, player.slot); + break; + } + case P_CL2LS_REQ_CHAR_DELETE: { + if (data->size != sizeof(sP_CL2LS_REQ_CHAR_DELETE)) + return; - character->PCStyle.iNameCheck = 1; - resp.sPC_Style = character->PCStyle; - resp.sPC_Style2.iAppearanceFlag = 1; - resp.sPC_Style2.iTutorialFlag = 0; - resp.sPC_Style2.iPayzoneFlag = 1; - resp.iLevel = 36; - resp.sOn_Item = character->sOn_Item; - - loginSessions[sock].characters[UID] = Player(); - loginSessions[sock].characters[UID].level = 36; - loginSessions[sock].characters[UID].FEKey = sock->getFEKey(); - loginSessions[sock].characters[UID].PCStyle = character->PCStyle; - loginSessions[sock].characters[UID].PCStyle2.iAppearanceFlag = 1; - loginSessions[sock].characters[UID].PCStyle2.iPayzoneFlag = 1; - loginSessions[sock].characters[UID].PCStyle2.iTutorialFlag = 1; - loginSessions[sock].characters[UID].x = settings::SPAWN_X; - loginSessions[sock].characters[UID].y = settings::SPAWN_Y; - loginSessions[sock].characters[UID].z = settings::SPAWN_Z; - loginSessions[sock].characters[UID].Equip[1].iID = character->sOn_Item.iEquipUBID; // upper body - loginSessions[sock].characters[UID].Equip[1].iType = 1; - loginSessions[sock].characters[UID].Equip[2].iID = character->sOn_Item.iEquipLBID; // lower body - loginSessions[sock].characters[UID].Equip[2].iType = 2; - loginSessions[sock].characters[UID].Equip[3].iID = character->sOn_Item.iEquipFootID; // foot! - loginSessions[sock].characters[UID].Equip[3].iType = 3; - loginSessions[sock].characters[UID].IsGM = false; - loginSessions[sock].characters[UID].Equip[3].iType = 3; - loginSessions[sock].characters[UID].isTrading = false; - loginSessions[sock].characters[UID].isTradeConfirm = false; - loginSessions[sock].characters[UID].IsGM = settings::GM; - - - sock->sendPacket((void*)&resp, P_LS2CL_REP_CHAR_CREATE_SUCC, sizeof(sP_LS2CL_REP_CHAR_CREATE_SUCC)); - break; - } - case P_CL2LS_REQ_CHAR_DELETE: { - if (data->size != sizeof(sP_CL2LS_REQ_CHAR_DELETE)) - return; - - sP_CL2LS_REQ_CHAR_DELETE* del = (sP_CL2LS_REQ_CHAR_DELETE*)data->buf; - int operationResult = Database::deleteCharacter(del->iPC_UID, loginSessions[sock].userID); - - // success - if (operationResult > 0) { + sP_CL2LS_REQ_CHAR_DELETE* del = (sP_CL2LS_REQ_CHAR_DELETE*)data->buf; + int operationResult = Database::deleteCharacter(del->iPC_UID); + INITSTRUCT(sP_LS2CL_REP_CHAR_DELETE_SUCC, resp); resp.iSlotNum = operationResult; sock->sendPacket((void*)&resp, P_LS2CL_REP_CHAR_DELETE_SUCC, sizeof(sP_LS2CL_REP_CHAR_DELETE_SUCC)); - } else { - // failure - // client doesnt't care about this packet and softlocks - INITSTRUCT(sP_LS2CL_REP_CHAR_DELETE_FAIL, resp); - resp.iErrorCode = 0; - sock->sendPacket((void*)&resp, P_LS2CL_REP_CHAR_DELETE_FAIL, sizeof(sP_LS2CL_REP_CHAR_DELETE_FAIL)); + + break; } + case P_CL2LS_REQ_CHAR_SELECT: { + if (data->size != sizeof(sP_CL2LS_REQ_CHAR_SELECT)) + return; + + // character selected + sP_CL2LS_REQ_CHAR_SELECT* chararacter = (sP_CL2LS_REQ_CHAR_SELECT*)data->buf; + INITSTRUCT(sP_LS2CL_REP_CHAR_SELECT_SUCC, resp); - break; - } - case P_CL2LS_REQ_CHAR_SELECT: { - if (data->size != sizeof(sP_CL2LS_REQ_CHAR_SELECT)) - return; + DEBUGLOG( + std::cout << "P_CL2LS_REQ_CHAR_SELECT:" << std::endl; + std::cout << "\tPC_UID: " << chararacter->iPC_UID << std::endl; + ) - // character selected - sP_CL2LS_REQ_CHAR_SELECT* chararacter = (sP_CL2LS_REQ_CHAR_SELECT*)data->buf; - INITSTRUCT(sP_LS2CL_REP_CHAR_SELECT_SUCC, resp); + loginSessions[sock].selectedChar = chararacter->iPC_UID; + Database::updateSelected(loginSessions[sock].userID, loginSessions[sock].characters[chararacter->iPC_UID].slot); + sock->sendPacket((void*)&resp, P_LS2CL_REP_CHAR_SELECT_SUCC, sizeof(sP_LS2CL_REP_CHAR_SELECT_SUCC)); + break; + } + case P_CL2LS_REQ_SHARD_SELECT: { + if (data->size != sizeof(sP_CL2LS_REQ_SHARD_SELECT)) + return; + + // tell client to connect to the shard server + sP_CL2LS_REQ_SHARD_SELECT* shard = (sP_CL2LS_REQ_SHARD_SELECT*)data->buf; + INITSTRUCT(sP_LS2CL_REP_SHARD_SELECT_SUCC, resp); - DEBUGLOG( - std::cout << "P_CL2LS_REQ_CHAR_SELECT:" << std::endl; - std::cout << "\tPC_UID: " << chararacter->iPC_UID << std::endl; - ) + DEBUGLOG( + std::cout << "P_CL2LS_REQ_SHARD_SELECT:" << std::endl; + std::cout << "\tShard: " << (int)shard->ShardNum << std::endl; + ) - loginSessions[sock].selectedChar = chararacter->iPC_UID; + const char* SHARD_IP = settings::SHARDSERVERIP.c_str(); + resp.iEnterSerialKey = rand(); + resp.g_FE_ServerPort = settings::SHARDPORT; - sock->sendPacket((void*)&resp, P_LS2CL_REP_CHAR_SELECT_SUCC, sizeof(sP_LS2CL_REP_CHAR_SELECT_SUCC)); - break; - } - case P_CL2LS_REQ_SHARD_SELECT: { - if (data->size != sizeof(sP_CL2LS_REQ_SHARD_SELECT)) - return; + // copy IP to resp (this struct uses ASCII encoding so we don't have to goof around converting encodings) + memcpy(resp.g_FE_ServerIP, SHARD_IP, strlen(SHARD_IP)); + resp.g_FE_ServerIP[strlen(SHARD_IP)] = '\0'; - // tell client to connect to the shard server - sP_CL2LS_REQ_SHARD_SELECT* shard = (sP_CL2LS_REQ_SHARD_SELECT*)data->buf; - INITSTRUCT(sP_LS2CL_REP_SHARD_SELECT_SUCC, resp); + // pass player to CNSharedData + CNSharedData::setPlayer(resp.iEnterSerialKey, loginSessions[sock].characters[loginSessions[sock].selectedChar]); - DEBUGLOG( - std::cout << "P_CL2LS_REQ_SHARD_SELECT:" << std::endl; - std::cout << "\tShard: " << (int)shard->ShardNum << std::endl; - ) + sock->sendPacket((void*)&resp, P_LS2CL_REP_SHARD_SELECT_SUCC, sizeof(sP_LS2CL_REP_SHARD_SELECT_SUCC)); + break; + } + case P_CL2LS_REQ_SAVE_CHAR_TUTOR: { + if (data->size != sizeof(sP_CL2LS_REQ_SAVE_CHAR_TUTOR)) + return; + sP_CL2LS_REQ_SAVE_CHAR_TUTOR* save = (sP_CL2LS_REQ_SAVE_CHAR_TUTOR*)data->buf; + Database::finishTutorial(save->iPC_UID); + loginSessions[sock].characters[save->iPC_UID].PCStyle2.iTutorialFlag = 1; + loginSessions[sock].characters[save->iPC_UID].Equip[0].iID = 328; + loginSessions[sock].characters[save->iPC_UID].Equip[0].iType = 0; + loginSessions[sock].characters[save->iPC_UID].Equip[0].iOpt = 1; - const char* SHARD_IP = settings::SHARDSERVERIP.c_str(); - resp.iEnterSerialKey = rand(); - resp.g_FE_ServerPort = settings::SHARDPORT; - - // copy IP to resp (this struct uses ASCII encoding so we don't have to goof around converting encodings) - memcpy(resp.g_FE_ServerIP, SHARD_IP, strlen(SHARD_IP)); - resp.g_FE_ServerIP[strlen(SHARD_IP)] = '\0'; - - // pass player to CNSharedData - CNSharedData::setPlayer(resp.iEnterSerialKey, loginSessions[sock].characters[loginSessions[sock].selectedChar]); - - sock->sendPacket((void*)&resp, P_LS2CL_REP_SHARD_SELECT_SUCC, sizeof(sP_LS2CL_REP_SHARD_SELECT_SUCC)); - break; - } - case P_CL2LS_REQ_SAVE_CHAR_TUTOR: { - if (data->size != sizeof(sP_CL2LS_REQ_SAVE_CHAR_TUTOR)) - return; - - sP_CL2LS_REQ_SAVE_CHAR_TUTOR* save = (sP_CL2LS_REQ_SAVE_CHAR_TUTOR*)data->buf; - Database::finishTutorial(save->iPC_UID); - loginSessions[sock].characters[save->iPC_UID].PCStyle2.iTutorialFlag = 1; - loginSessions[sock].characters[save->iPC_UID].Equip[0].iID = 328; - loginSessions[sock].characters[save->iPC_UID].Equip[0].iType = 0; - loginSessions[sock].characters[save->iPC_UID].Equip[0].iOpt = 1; - - break; - } - default: - if (settings::VERBOSITY > 0) - std::cerr << "OpenFusion: LOGIN UNIMPLM ERR. PacketType: " << Defines::p2str(CL2LS, data->type) << " (" << data->type << ")" << std::endl; - break; + break; + } + case P_CL2LS_REQ_CHANGE_CHAR_NAME: { + if (data->size != sizeof(sP_CL2LS_REQ_CHANGE_CHAR_NAME)) + return; + sP_CL2LS_REQ_CHANGE_CHAR_NAME* save = (sP_CL2LS_REQ_CHANGE_CHAR_NAME*)data->buf; + Database::changeName(save); + INITSTRUCT(sP_LS2CL_REP_CHANGE_CHAR_NAME_SUCC, resp); + resp.iPC_UID = save->iPCUID; + memcpy(resp.szFirstName, save->szFirstName, sizeof(char16_t)*9); + memcpy(resp.szLastName, save->szLastName, sizeof(char16_t) * 17); + resp.iSlotNum = save->iSlotNum; + sock->sendPacket((void*)&resp, P_LS2CL_REP_CHANGE_CHAR_NAME_SUCC, sizeof(sP_LS2CL_REP_CHANGE_CHAR_NAME_SUCC)); + break; + } + case P_CL2LS_REQ_PC_EXIT_DUPLICATE:{ + if (data->size != sizeof(sP_CL2LS_REQ_PC_EXIT_DUPLICATE)) + return; + sP_CL2LS_REQ_PC_EXIT_DUPLICATE* exit = (sP_CL2LS_REQ_PC_EXIT_DUPLICATE*)data->buf; + auto account = Database::findAccount(U16toU8(exit->szID)); + if (account == nullptr) + break; + int accountId = account->AccountID; + if (!exitDuplicate(accountId)) + PlayerManager::exitDuplicate(accountId); + break; + } + default: + if (settings::VERBOSITY) + std::cerr << "OpenFusion: LOGIN UNIMPLM ERR. PacketType: " << Defines::p2str(CL2LS, data->type) << " (" << data->type << ")" << std::endl; + break; + /* Unimplemented CL2LS packets: + P_CL2LS_CHECK_NAME_LIST - unused by the client + P_CL2LS_REQ_SERVER_SELECT + P_CL2LS_REQ_SHARD_LIST_INFO - dev commands, useless as we only run 1 server + */ } } @@ -357,3 +368,42 @@ void CNLoginServer::newConnection(CNSocket* cns) { void CNLoginServer::killConnection(CNSocket* cns) { loginSessions.erase(cns); } + +#pragma region helperMethods +bool CNLoginServer::isAccountInUse(int accountId) { + std::map::iterator it; + for (it = CNLoginServer::loginSessions.begin(); it != CNLoginServer::loginSessions.end(); it++) + { + if (it->second.userID == accountId) + return true; + } + return false; +} +bool CNLoginServer::exitDuplicate(int accountId) +{ + std::map::iterator it; + for (it = CNLoginServer::loginSessions.begin(); it != CNLoginServer::loginSessions.end(); it++) + { + if (it->second.userID == accountId) + { + CNSocket* sock = it->first; + INITSTRUCT(sP_LS2CL_REP_PC_EXIT_DUPLICATE, resp); + resp.iErrorCode = 0; + sock->sendPacket((void*)&resp, P_LS2CL_REP_PC_EXIT_DUPLICATE, sizeof(sP_LS2CL_REP_PC_EXIT_DUPLICATE)); + sock->kill(); + return true; + } + } + return false; +} +bool CNLoginServer::isLoginDataGood(std::string login, std::string password) +{ + std::regex loginRegex("^([A-Za-z\\d_\\-]){5,20}$"); + std::regex passwordRegex("^([A-Za-z\\d_\\-@$!%*#?&,.+:;<=>]){8,20}$"); + return (std::regex_match(login, loginRegex) && std::regex_match(password, passwordRegex)); +} +bool CNLoginServer::isPasswordCorrect(std::string actualPassword, std::string tryPassword) +{ + return BCrypt::validatePassword(tryPassword, actualPassword); +} +#pragma endregion helperMethods diff --git a/src/CNLoginServer.hpp b/src/CNLoginServer.hpp index 28d789f..d76722f 100644 --- a/src/CNLoginServer.hpp +++ b/src/CNLoginServer.hpp @@ -9,7 +9,7 @@ struct CNLoginData { std::map characters; int64_t selectedChar; - int userID; + int userID; int slot; }; enum class LOGINERRORID { @@ -29,10 +29,15 @@ class CNLoginServer : public CNServer { private: static void handlePacket(CNSocket* sock, CNPacketData* data); static std::map loginSessions; - + + static bool isLoginDataGood(std::string login, std::string password); + static bool isPasswordCorrect(std::string actualPassword, std::string tryPassword); + static bool isAccountInUse(int accountId); + //returns true if success + static bool exitDuplicate(int accountId); public: CNLoginServer(uint16_t p); void newConnection(CNSocket* cns); - void killConnection(CNSocket* cns); + void killConnection(CNSocket* cns); }; diff --git a/src/Database.cpp b/src/Database.cpp index 6caba34..6550c1c 100644 --- a/src/Database.cpp +++ b/src/Database.cpp @@ -1,424 +1,356 @@ -#include "contrib/sqlite/sqlite3pp.h" +#include "Database.hpp" #include "contrib/bcrypt/BCrypt.hpp" #include "CNProtocol.hpp" -#include "Database.hpp" +#include +#include "contrib/JSON.hpp" #include "CNStructs.hpp" #include "settings.hpp" #include "Player.hpp" -#include -#include -#include "contrib/JSON.hpp" +#include "CNStructs.hpp" +#include "contrib/sqlite/sqlite_orm.h" +using namespace sqlite_orm; -//TODO: replace this sqlite wrapper with something better, clean up queries, get rid of json +# pragma region DatabaseScheme +auto db = make_storage("database.db", + make_table("Accounts", + make_column("AccountID", &Database::Account::AccountID, autoincrement(), primary_key()), + make_column("Login", &Database::Account::Login), + make_column("Password", &Database::Account::Password), + make_column("Selected", &Database::Account::Selected) + ), + make_table("Players", + make_column("PlayerID", &Database::DbPlayer::PlayerID, autoincrement(), primary_key()), + make_column("AccountID", &Database::DbPlayer::AccountID), + make_column("Slot", &Database::DbPlayer::slot), + make_column("Firstname", &Database::DbPlayer::FirstName), + make_column("LastName", &Database::DbPlayer::LastName), + make_column("Level", &Database::DbPlayer::Level), + make_column("AppearanceFlag", &Database::DbPlayer::AppearanceFlag), + make_column("TutorialFlag", &Database::DbPlayer::TutorialFlag), + make_column("PayZoneFlag", &Database::DbPlayer::PayZoneFlag), + make_column("XCoordinates", &Database::DbPlayer::x_coordinates), + make_column("YCoordinates", &Database::DbPlayer::y_coordinates), + make_column("ZCoordinates", &Database::DbPlayer::z_coordinates), + make_column("Body", &Database::DbPlayer::Body), + make_column("Class", &Database::DbPlayer::Class), + make_column("EquipFoot", &Database::DbPlayer::EquipFoot), + make_column("EquipLB", &Database::DbPlayer::EquipLB), + make_column("EquipUB", &Database::DbPlayer::EquipUB), + make_column("EquipWeapon1", &Database::DbPlayer::EquipWeapon1), + make_column("EyeColor", &Database::DbPlayer::EyeColor), + make_column("FaceStyle", &Database::DbPlayer::FaceStyle), + make_column("Gender", &Database::DbPlayer::Gender), + make_column("HP", &Database::DbPlayer::HP), + make_column("HairColor", &Database::DbPlayer::HairColor), + make_column("HairStyle", &Database::DbPlayer::HairStyle), + make_column("Height", &Database::DbPlayer::Height), + make_column("NameCheck", &Database::DbPlayer::NameCheck), + make_column("SkinColor", &Database::DbPlayer::SkinColor), + make_column("isGM", &Database::DbPlayer::isGM), + make_column("FusionMatter", &Database::DbPlayer::FusionMatter), + make_column("Taros", &Database::DbPlayer::Taros) + ), + make_table("Inventory", + make_column("AccountID", &Database::Inventory::AccountID, primary_key()) + ) +); -sqlite3pp::database db; +# pragma endregion DatabaseScheme -void Database::open() { - //checking if database file exists - std::ifstream file; - file.open("data.db"); +#pragma region LoginServer - if (file) { - file.close(); - // if exists, assign it - db = sqlite3pp::database("data.db"); - DEBUGLOG(std::cout << "[DB] Database in operation" << std::endl;) - } - else { - // if doesn't, create all the tables - DEBUGLOG(std::cout << "[DB] Creating new database" << std::endl;) - db = sqlite3pp::database("data.db"); - - // creates accounts - db.execute("CREATE TABLE Accounts(AccountID INTEGER PRIMARY KEY AUTOINCREMENT, Login TEXT NOT NULL, Password TEXT NOT NULL);"); - // creates characters - db.execute("CREATE TABLE Players(PlayerID INTEGER PRIMARY KEY AUTOINCREMENT, AccountID INTEGER NOT NULL, Slot INTEGER NOT NULL, FirstName TEXT NOT NULL, LastName TEXT NOT NULL, CharData TEXT NOT NULL);"); - - DEBUGLOG(std::cout << "Done" << std::endl;) - } +void Database::open() +{ + //this parameter means it will try to preserve data during migration + bool preserve = true; + db.sync_schema(preserve); + DEBUGLOG( + std::cout << "[DB] Database in operation" << std::endl; + ) } -// verifies that the username & passwords are valid -bool Database::isLoginDataGood(std::string login, std::string password) { - std::regex loginRegex("^([A-Za-z\\d_\\-]){5,20}$"); - std::regex passwordRegex("^([A-Za-z\\d_\\-@$!%*#?&,.+:;<=>]){2,20}$"); - return (std::regex_match(login, loginRegex) && std::regex_match(password, passwordRegex)); -} - -void Database::addAccount(std::string login, std::string password) { - // generates prepared statment - sqlite3pp::command cmd(db, "INSERT INTO Accounts (Login, Password) VALUES (:login, :password)"); - - // generates a hashed password! +int Database::addAccount(std::string login, std::string password) +{ password = BCrypt::generateHash(password); - - // binds args to the command - cmd.bind(":login", login, sqlite3pp::nocopy); - cmd.bind(":password", password, sqlite3pp::nocopy); - cmd.execute(); + Account x; + x.Login = login; + x.Password = password; + x.Selected = 1; + return db.insert(x); } -bool Database::doesUserExist(std::string login) { - // generates prepared statement - sqlite3pp::query qry(db, "SELECT COUNT(AccountID) FROM Accounts WHERE Login = :login"); - // binds to the query - qry.bind(":login", login, sqlite3pp::nocopy); - - // executes - sqlite3pp::query::iterator i = qry.begin(); - - // grabs the first result (the count) - int result; - std::tie(result) = (*i).get_columns(0); - - // if there is more than 0 results, the user exists :eyes: - return (result > 0); +void Database::updateSelected(int accountId, int slot) +{ + Account acc = db.get(accountId); + acc.Selected = slot; + db.update(acc); } -bool Database::isPasswordCorrect(std::string login, std::string password) { - // generates prepared statement - sqlite3pp::query qry(db, "SELECT Password FROM Accounts WHERE Login = :login"); - // binds username to the query - qry.bind(":login", login, sqlite3pp::nocopy); - - // executes - sqlite3pp::query::iterator i = qry.begin(); - - // grabs the first result - const char* actual; - std::tie(actual) = (*i).get_columns(0); - - // validate password hash with provded password - return BCrypt::validatePassword(password, actual); +std::unique_ptr Database::findAccount(std::string login) +{ + //this is awful, I've tried everything to improve it + auto find = db.get_all( + where(c(&Account::Login) == login), limit(1)); + if (find.empty()) + return nullptr; + return + std::unique_ptr(new Account(find.front())); } -int Database::getUserID(std::string login) { - // generates prep statement - sqlite3pp::query qry(db, "SELECT AccountID FROM Accounts WHERE Login = :login"); - // binds the username to the login param - qry.bind(":login", login, sqlite3pp::nocopy); - - // executes the query - sqlite3pp::query::iterator i = qry.begin(); - - // grabs the first result of the query - int result; - std::tie(result) = (*i).get_columns(0); - - // returns the result - return result; +bool Database::isNameFree(sP_CL2LS_REQ_CHECK_CHAR_NAME* nameCheck) +{ + //TODO: add colate nocase + std::string First = U16toU8(nameCheck->szFirstName); + std::string Last = U16toU8(nameCheck->szLastName); + return + (db.get_all + (where((c(&DbPlayer::FirstName) == First) + and (c(&DbPlayer::LastName) == Last))) + .empty()); } -int Database::getUserSlotsNum(int AccountId) { - // generates the prepared statement - sqlite3pp::query qry(db, "SELECT COUNT(PlayerID) FROM Players WHERE AccountID = :ID"); - - // binds the ID to the param - qry.bind(":ID", AccountId); - - // executes - sqlite3pp::query::iterator i = qry.begin(); - - // grabs the first result - int result; - std::tie(result) = (*i).get_columns(0); - - // returns the result - return result; -} - -bool Database::isNameFree(std::string First, std::string Second) { - // generates the prepared statement - sqlite3pp::query qry(db, "SELECT COUNT(PlayerID) FROM Players WHERE FirstName = :First COLLATE nocase AND LastName = :Second COLLATE nocase"); - - // binds the params - qry.bind(":First", First, sqlite3pp::nocopy); - qry.bind(":Second", Second, sqlite3pp::nocopy); - - // executes the query - sqlite3pp::query::iterator i = qry.begin(); - - // grabs the result - int result; - std::tie(result) = (*i).get_columns(0); - - // if no results return, the the username is unused - return (result == 0); -} - -void Database::createCharacter(sP_CL2LS_REQ_SAVE_CHAR_NAME* save, int AccountID) { - // generate the command - sqlite3pp::command cmd(db, "INSERT INTO Players (AccountID, Slot, FirstName, LastName, CharData) VALUES (:AccountID, :Slot, :FirstName, :LastName, :CharData)"); - - // generate character data - std::string charData = CharacterToJson(save); - std::string first = U16toU8(save->szFirstName); - std::string last = U16toU8(save->szLastName); - - // bind to command - cmd.bind(":AccountID", AccountID); - cmd.bind(":Slot", save->iSlotNum); - cmd.bind(":CharData", charData, sqlite3pp::nocopy); - cmd.bind(":FirstName", first, sqlite3pp::nocopy); - cmd.bind(":LastName", last, sqlite3pp::nocopy); - - // run - cmd.execute(); -} - -void Database::finishCharacter(sP_CL2LS_REQ_CHAR_CREATE* character) { - sqlite3pp::command cmd(db, "UPDATE Players SET CharData = :data WHERE PlayerID = :id"); - - // grab the data to add to the command - int id = character->PCStyle.iPC_UID; - std::string charData = CharacterToJson(character); - - // bind to the command & execute - cmd.bind(":data", charData, sqlite3pp::nocopy); - cmd.bind(":id", id); - cmd.execute(); -} - -void Database::finishTutorial(int PlayerID) { - sqlite3pp::query qry(db, "SELECT CharData FROM Players WHERE PlayerID = :ID"); - - // bind to the query and execute - qry.bind(":ID", PlayerID); - sqlite3pp::query::iterator i = qry.begin(); - - // grab the player json from the database - std::string json; - std::tie(json) = (*i).get_columns(0); - - // parse the json into a Player - Player player = JsonToPlayer(json, PlayerID); - - // set the tutorial flag & equip lightning gun - player.PCStyle2.iTutorialFlag = 1; - player.Equip[0].iID = 328; - json = PlayerToJson(player); - - // update the database - sqlite3pp::command cmd(db, "UPDATE Players SET CharData = :data WHERE PlayerID = :id"); - cmd.bind(":data", json, sqlite3pp::nocopy); - cmd.bind(":id", PlayerID); - cmd.execute(); -} - -int Database::getCharacterID(int AccountID, int slot) { - // make the query - sqlite3pp::query qry(db, "SELECT PlayerID FROM Players WHERE AccountID = :ID AND Slot = :Slot"); - - // bind the params & execute - qry.bind(":ID", AccountID); - qry.bind(":Slot", slot); - sqlite3pp::query::iterator i = qry.begin(); - - // grab the result - int result; - std::tie(result) = (*i).get_columns(0); - - return result; -} - -int Database::deleteCharacter(int characterID, int accountID) { - // checking if requested player exist and is bound to the account - - sqlite3pp::query qry(db, "SELECT COUNT(AccountID) FROM Players WHERE AccountID = :AccID AND PlayerID = :PID"); - - qry.bind(":AccID", accountID); - qry.bind(":PID", characterID); - sqlite3pp::query::iterator i = qry.begin(); - - int result; - std::tie(result) = (*i).get_columns(0); - - if (result > 0) { - // get player character slot - sqlite3pp::query qry(db, "SELECT Slot FROM Players WHERE PlayerID = :PID"); - - // bind & execute - qry.bind(":PID", characterID); - sqlite3pp::query::iterator i = qry.begin(); - - // grab the slot to return - int slot; - std::tie(slot) = (*i).get_columns(0); - - // actually delete the record - sqlite3pp::command cmd(db, "DELETE FROM Players WHERE PlayerID = :ID"); - cmd.bind(":ID", characterID); - cmd.execute(); - - // finally, return the grabbed slot - return slot; - } - - return -1; -} - -// TODO: this should really be std::vector, but for the sake of compatibility with PRs, I'll change this after merging -std::list Database::getCharacters(int userID) { - std::list result = std::list(); - - sqlite3pp::query qry(db, "SELECT * FROM Players WHERE AccountID = :ID"); - - qry.bind(":ID", userID); - - // for each character owned by the account, - for (sqlite3pp::query::iterator i = qry.begin(); i != qry.end(); ++i) { - // grab the data - int ID, AccountID, slot; - char const* charData, * first, * second; - std::tie(ID, AccountID, slot, first, second, charData) = - (*i).get_columns(0, 1, 2, 3, 4, 5); - - // convert to player - Player toadd = JsonToPlayer(charData, ID); - toadd.slot = slot; - - // add it to the results - result.push_back(toadd); - } - return result; -} - -std::string Database::CharacterToJson(sP_CL2LS_REQ_SAVE_CHAR_NAME* save) { - nlohmann::json json = { - {"Level",36}, - //to check - {"HP",1000}, - {"NameCheck", 1}, - {"FirstName",U16toU8(save->szFirstName)}, - {"LastName",U16toU8(save->szLastName)}, - {"Gender",rand() % 2 + 1 }, - {"FaceStyle",1}, - {"HairStyle",1}, - {"HairColor",(rand() % 19) + 1}, - {"SkinColor",(rand() % 13) + 1}, - {"EyeColor",(rand() % 6) + 1}, - {"Height",(rand() % 6)}, - {"Body",(rand() % 4)}, - {"Class",0}, - {"AppearanceFlag",0}, - {"PayzoneFlag",1}, - {"TutorialFlag",0}, - {"EquipUB", 217}, - {"EquipLB", 203}, - {"EquipFoot", 314}, - {"EquipWeapon", 0}, - {"x",settings::SPAWN_X}, - {"y",settings::SPAWN_Y}, - {"z",settings::SPAWN_Z}, - {"isGM",false}, - }; - return json.dump(); -} - -std::string Database::PlayerToJson(Player player) { - nlohmann::json json = { - {"Level",player.level}, - //to check - {"HP",player.HP}, - {"NameCheck", 1}, - {"FirstName",U16toU8(player.PCStyle.szFirstName)}, - {"LastName",U16toU8(player.PCStyle.szLastName)}, - {"Gender",player.PCStyle.iGender}, - {"FaceStyle",player.PCStyle.iFaceStyle}, - {"HairStyle",player.PCStyle.iHairStyle}, - {"HairColor",player.PCStyle.iHairColor}, - {"SkinColor",player.PCStyle.iSkinColor}, - {"EyeColor",player.PCStyle.iEyeColor}, - {"Height",player.PCStyle.iHeight}, - {"Body",player.PCStyle.iBody}, - {"Class",player.PCStyle.iClass}, - {"AppearanceFlag",player.PCStyle2.iAppearanceFlag}, - {"PayzoneFlag",player.PCStyle2.iPayzoneFlag}, - {"TutorialFlag",player.PCStyle2.iTutorialFlag}, - {"EquipUB", player.Equip[1].iID}, - {"EquipLB", player.Equip[2].iID}, - {"EquipFoot", player.Equip[3].iID}, - {"EquipWeapon", player.Equip[0].iID}, - {"x",player.x}, - {"y",player.y}, - {"z",player.z}, - {"isGM",false}, - }; - return json.dump(); -} - -Player Database::JsonToPlayer(std::string input, int PC_UID) { - std::string err; - const auto json = nlohmann::json::parse(input); - Player player; - player.PCStyle.iPC_UID = (int64_t)PC_UID; - player.level = std::stoi(json["Level"].dump()); - player.HP = std::stoi(json["HP"].dump()); - player.PCStyle.iNameCheck = std::stoi(json["NameCheck"].dump()); - U8toU16(json["FirstName"].get(), player.PCStyle.szFirstName); - U8toU16(json["LastName"].get(), player.PCStyle.szLastName); - player.PCStyle.iGender = std::stoi(json["Gender"].dump()); - player.PCStyle.iFaceStyle = std::stoi(json["FaceStyle"].dump()); - player.PCStyle.iHairStyle = std::stoi(json["HairStyle"].dump()); - player.PCStyle.iHairColor = std::stoi(json["HairColor"].dump()); - player.PCStyle.iSkinColor = std::stoi(json["SkinColor"].dump()); - player.PCStyle.iEyeColor = std::stoi(json["EyeColor"].dump()); - player.PCStyle.iHeight = std::stoi(json["Height"].dump()); - player.PCStyle.iBody = std::stoi(json["Body"].dump()); - player.PCStyle.iClass = std::stoi(json["Class"].dump()); - player.PCStyle2.iAppearanceFlag = std::stoi(json["AppearanceFlag"].dump()); - player.PCStyle2.iPayzoneFlag = std::stoi(json["PayzoneFlag"].dump()); - player.PCStyle2.iTutorialFlag = std::stoi(json["TutorialFlag"].dump()); - player.Equip[0].iID = std::stoi(json["EquipWeapon"].dump()); - if (player.Equip[0].iID != 0) - player.Equip[0].iOpt = 1; +int Database::createCharacter(sP_CL2LS_REQ_SAVE_CHAR_NAME* save, int AccountID) +{ + DbPlayer create; + //save packet data + create.FirstName = U16toU8(save->szFirstName); + create.LastName = U16toU8(save->szLastName); + create.slot = save->iSlotNum; + create.AccountID = AccountID; + //set flags + create.AppearanceFlag = 0; + create.TutorialFlag = 0; + create.PayZoneFlag = 0; + //set namecheck based on setting + if (settings::APPROVEALLNAMES || save->iFNCode) + create.NameCheck = 1; else - player.Equip[0].iOpt = 0; - player.Equip[0].iType = 0; - player.Equip[1].iID = std::stoi(json["EquipUB"].dump()); - player.Equip[1].iOpt = 1; - player.Equip[1].iType = 1; - player.Equip[2].iID = std::stoi(json["EquipLB"].dump()); - player.Equip[2].iOpt = 1; - player.Equip[2].iType = 2; - player.Equip[3].iID = std::stoi(json["EquipFoot"].dump()); - player.Equip[3].iOpt = 1; - player.Equip[3].iType = 3; - player.x = std::stoi(json["x"].dump()); - player.y = std::stoi(json["y"].dump()); - player.z = std::stoi(json["z"].dump()); - return player; + create.NameCheck = 0; + //create default body character + create.Body= 0; + create.Class= 0; + create.EquipFoot= 0; + create.EquipLB= 0; + create.EquipUB= 0; + create.EquipWeapon1= 0; + create.EquipWeapon2= 0; + create.EyeColor= 1; + create.FaceStyle= 1; + create.Gender= 1; + create.HP= 1000; + create.HairColor= 1; + create.HairStyle = 1; + create.Height= 0; + create.Level= 1; + create.SkinColor= 1; + create.isGM = false; + //commented and disabled for now + //if (U16toU8(save->szFirstName) == settings::GMPASS) { + // create.isGM = true; + //} + + create.FusionMatter= 0; + create.Taros= 0; + create.x_coordinates = settings::SPAWN_X; + create.y_coordinates= settings::SPAWN_Y; + create.z_coordinates= settings::SPAWN_Z; + return db.insert(create); } -std::string Database::CharacterToJson(sP_CL2LS_REQ_CHAR_CREATE* character) { - nlohmann::json json = { - {"Level",36}, - //to check - {"HP",1000}, - {"NameCheck", 1}, - {"FirstName",U16toU8(character->PCStyle.szFirstName)}, - {"LastName",U16toU8(character->PCStyle.szLastName)}, - {"Gender",character->PCStyle.iGender}, - {"FaceStyle",character->PCStyle.iFaceStyle}, - {"HairStyle",character->PCStyle.iHairStyle}, - {"HairColor",character->PCStyle.iHairColor}, - {"SkinColor",character->PCStyle.iSkinColor}, - {"EyeColor",character->PCStyle.iEyeColor}, - {"Height",character->PCStyle.iHeight}, - {"Body",character->PCStyle.iBody}, - {"Class",character->PCStyle.iClass}, - {"AppearanceFlag",1}, - {"PayzoneFlag",1}, - {"TutorialFlag",0}, - {"EquipUB", character->sOn_Item.iEquipUBID}, - {"EquipLB", character->sOn_Item.iEquipLBID}, - {"EquipFoot", character->sOn_Item.iEquipFootID}, - {"EquipWeapon", 0}, - {"x",settings::SPAWN_X}, - {"y",settings::SPAWN_Y}, - {"z",settings::SPAWN_Z}, - {"isGM",false}, - }; - return json.dump(); +void Database::finishCharacter(sP_CL2LS_REQ_CHAR_CREATE* character) +{ + DbPlayer finish = getDbPlayerById(character->PCStyle.iPC_UID); + finish.AppearanceFlag = 1; + finish.Body = character->PCStyle.iBody; + finish.Class = character->PCStyle.iClass; + finish.EquipFoot = character->sOn_Item.iEquipFootID; + finish.EquipLB = character->sOn_Item.iEquipLBID; + finish.EquipUB = character->sOn_Item.iEquipUBID; + finish.EyeColor = character->PCStyle.iEyeColor; + finish.FaceStyle = character->PCStyle.iFaceStyle; + finish.Gender = character->PCStyle.iGender; + finish.HairColor = character->PCStyle.iHairColor; + finish.HairStyle = character->PCStyle.iHairStyle; + finish.Height = character->PCStyle.iHeight; + finish.Level = 1; + finish.SkinColor = character->PCStyle.iSkinColor; + db.update(finish); } + +void Database::finishTutorial(int PlayerID) +{ + DbPlayer finish = getDbPlayerById(PlayerID); + finish.TutorialFlag = 1; + //equip lightning gun + finish.EquipWeapon1 = 328; + db.update(finish); +} + +int Database::deleteCharacter(int characterID) +{ + auto find = + db.get_all(where(c(&DbPlayer::PlayerID) == characterID)); + int slot = find.front().slot; + db.remove(find.front().PlayerID); + return slot; +} + +std::vector Database::getCharacters(int UserID) +{ + std::vectorcharacters = + db.get_all(where + (c(&DbPlayer::AccountID) == UserID)); + //parsing DbPlayer to Player + std::vector result = std::vector(); + for (auto &character : characters) { + Player toadd = DbToPlayer(character); + result.push_back( + toadd + ); + } + return result; +} + +void Database::evaluateCustomName(int characterID, CUSTOMNAME decision) { + DbPlayer player = getDbPlayerById(characterID); + player.NameCheck = (int)decision; + db.update(player); +} + +void Database::changeName(sP_CL2LS_REQ_CHANGE_CHAR_NAME* save) { + DbPlayer Player = getDbPlayerById(save->iPCUID); + Player.FirstName = U16toU8(save->szFirstName); + Player.LastName = U16toU8(save->szLastName); + if (settings::APPROVEALLNAMES || save->iFNCode) + Player.NameCheck = 1; + else + Player.NameCheck = 0; + db.update(Player); +} + +Database::DbPlayer Database::playerToDb(Player player) +{ + DbPlayer result; + result.PlayerID = player.iID; + result.AccountID = player.accountId; + result.AppearanceFlag = player.PCStyle2.iAppearanceFlag; + result.Body = player.PCStyle.iBody; + result.Class = player.PCStyle.iClass; + //equipment + result.EyeColor = player.PCStyle.iEyeColor; + result.FaceStyle = player.PCStyle.iFaceStyle; + result.FirstName = U16toU8( player.PCStyle.szFirstName); + //fm + result.Gender = player.PCStyle.iGender; + result.HairColor = player.PCStyle.iHairColor; + result.HairStyle = player.PCStyle.iHairStyle; + result.Height = player.PCStyle.iHeight; + result.HP = player.HP; + result.isGM = player.IsGM; + result.LastName = U16toU8(player.PCStyle.szLastName); + result.Level = player.level; + result.NameCheck = player.PCStyle.iNameCheck; + result.PayZoneFlag = player.PCStyle2.iPayzoneFlag; + result.PlayerID = player.PCStyle.iPC_UID; + result.SkinColor = player.PCStyle.iSkinColor; + result.slot = player.slot; + //taros + result.TutorialFlag = player.PCStyle2.iTutorialFlag; + result.x_coordinates = player.x; + result.y_coordinates = player.y; + result.z_coordinates = player.z; + + return result; +} + +Player Database::DbToPlayer(DbPlayer player) { + Player result; + + result.accountId = player.AccountID; + result.PCStyle2.iAppearanceFlag = player.AppearanceFlag; + result.PCStyle.iBody = player.Body; + result.PCStyle.iClass = player.Class; + result.PCStyle.iEyeColor = player.EyeColor; + result.PCStyle.iFaceStyle = player.FaceStyle; + U8toU16(player.FirstName, result.PCStyle.szFirstName); + result.PCStyle.iGender = player.Gender; + result.PCStyle.iHairColor = player.HairColor; + result.PCStyle.iHairStyle = player.HairStyle; + result.PCStyle.iHeight = player.Height; + result.HP = player.HP; + result.IsGM = player.isGM; + U8toU16(player.LastName, result.PCStyle.szLastName); + result.level = player.Level; + result.PCStyle.iNameCheck = player.NameCheck; + result.PCStyle2.iPayzoneFlag = player.PayZoneFlag; + result.iID = player.PlayerID; + result.PCStyle.iPC_UID = player.PlayerID; + result.PCStyle.iSkinColor = player.SkinColor; + result.slot = player.slot; + result.PCStyle2.iTutorialFlag = player.TutorialFlag; + result.x = player.x_coordinates; + result.y = player.y_coordinates; + result.z = player.z_coordinates; + + //TODO:: implement all of below + result.SerialKey = 0; + result.money = 0; + result.fusionmatter = 0; + result.activeNano = 0; + result.iPCState = 0; + result.equippedNanos[0] = 1; + result.equippedNanos[1] = 0; + result.equippedNanos[2] = 0; + result.isTrading = false; + result.isTradeConfirm = false; + + result.Nanos[1].iID = 1; + result.Nanos[1].iSkillID = 1; + result.Nanos[1].iStamina = 150; + + for (int i = 2; i < 37; i++) { + result.Nanos[i].iID = 0; + result.Nanos[i].iSkillID = 0; + result.Nanos[i].iStamina = 0; + } + + result.Equip[0].iID = player.EquipWeapon1; + result.Equip[0].iType = 0; + (player.EquipWeapon1) ? result.Equip[0].iOpt = 1 : result.Equip[0].iOpt = 0; + + result.Equip[1].iID = player.EquipUB; + result.Equip[1].iType = 1; + (player.EquipUB) ? result.Equip[1].iOpt = 1 : result.Equip[1].iOpt = 0; + + result.Equip[2].iID = player.EquipLB; + result.Equip[2].iType = 2; + (player.EquipLB) ? result.Equip[2].iOpt = 1 : result.Equip[2].iOpt = 0; + + result.Equip[3].iID = player.EquipFoot; + result.Equip[3].iType = 3; + (player.EquipFoot) ? result.Equip[3].iOpt = 1 : result.Equip[3].iOpt = 0; + + + + for (int i = 4; i < AEQUIP_COUNT; i++) { + // empty equips + result.Equip[i].iID = 0; + result.Equip[i].iType = i; + result.Equip[i].iOpt = 0; + } + for (int i = 0; i < AINVEN_COUNT; i++) { + // setup inventories + result.Inven[i].iID = 0; + result.Inven[i].iType = 0; + result.Inven[i].iOpt = 0; + } + return result; +} + +Database::DbPlayer Database::getDbPlayerById(int id) { + return db.get_all(where(c(&DbPlayer::PlayerID) == id)) + .front(); +} + +#pragma endregion LoginServer diff --git a/src/Database.hpp b/src/Database.hpp index 732b994..d7a2c02 100644 --- a/src/Database.hpp +++ b/src/Database.hpp @@ -1,31 +1,91 @@ #pragma once #include "CNStructs.hpp" #include "Player.hpp" -#include #include +#include namespace Database { - void open(); - //checks regex - bool isLoginDataGood(std::string login, std::string password); - void addAccount(std::string login, std::string password); - bool doesUserExist(std::string login); - bool isPasswordCorrect(std::string login, std::string password); - int getUserID(std::string login); - int getUserSlotsNum(int AccountId); - bool isNameFree(std::string First, std::string Second); - //after chosing name - void createCharacter(sP_CL2LS_REQ_SAVE_CHAR_NAME* save, int AccountID); - //after finishing creation - void finishCharacter(sP_CL2LS_REQ_CHAR_CREATE* character); - void finishTutorial(int PlayerID); - //returns slot nr if success - int deleteCharacter(int characterID, int accountID); - int getCharacterID(int AccountID, int slot); - std::list getCharacters(int userID); - //some json parsing crap - std::string CharacterToJson(sP_CL2LS_REQ_SAVE_CHAR_NAME* save); - std::string CharacterToJson(sP_CL2LS_REQ_CHAR_CREATE* character); - Player JsonToPlayer(std::string input, int PC_UID); - std::string PlayerToJson(Player player); -} \ No newline at end of file +#pragma region DatabaseStructs + + struct Account + { + int AccountID; + std::string Login; + std::string Password; + int Selected; + }; + struct Inventory + { + int AccountID; + }; + struct DbPlayer + { + int PlayerID; + int AccountID; + short int slot; + std::string FirstName; + std::string LastName; + short int AppearanceFlag; + short int Body; + short int Class; + short int EquipFoot; + short int EquipLB; + short int EquipUB; + short int EquipWeapon1; + short int EquipWeapon2; + short int EyeColor; + short int FaceStyle; + short int Gender; + int HP; + short int HairColor; + short int HairStyle; + short int Height; + short int Level; + short int NameCheck; + short int PayZoneFlag; + short int SkinColor; + bool TutorialFlag; + bool isGM; + int FusionMatter; + int Taros; + int x_coordinates; + int y_coordinates; + int z_coordinates; + }; + + + +#pragma endregion DatabaseStructs + + //handles migrations + void open(); + //returns ID + int addAccount(std::string login, std::string password); + void updateSelected(int accountId, int playerId); + std::unique_ptr findAccount(std::string login); + bool isNameFree(sP_CL2LS_REQ_CHECK_CHAR_NAME* nameCheck); + //called after chosing name, returns ID + int createCharacter(sP_CL2LS_REQ_SAVE_CHAR_NAME* save, int AccountID); + //called after finishing creation + void finishCharacter(sP_CL2LS_REQ_CHAR_CREATE* character); + //called after tutorial + void finishTutorial(int PlayerID); + //returns slot number + int deleteCharacter(int characterID); + std::vector getCharacters(int userID); + //accepting/declining custom name + enum class CUSTOMNAME { + approve = 1, + disapprove = 2 + }; + void evaluateCustomName(int characterID, CUSTOMNAME decision); + void changeName(sP_CL2LS_REQ_CHANGE_CHAR_NAME* save); + + //parsing DbPlayer + DbPlayer playerToDb(Player player); + Player DbToPlayer(DbPlayer player); + + //getting players + DbPlayer getDbPlayerById(int id); + +} diff --git a/src/Player.hpp b/src/Player.hpp index 3d780d1..a82ba99 100644 --- a/src/Player.hpp +++ b/src/Player.hpp @@ -7,6 +7,7 @@ #include "CNStructs.hpp" struct Player { + int accountId; int64_t SerialKey; int32_t iID; uint64_t FEKey; diff --git a/src/PlayerManager.cpp b/src/PlayerManager.cpp index 6980815..5d64db5 100644 --- a/src/PlayerManager.cpp +++ b/src/PlayerManager.cpp @@ -686,4 +686,28 @@ WarpLocation PlayerManager::getRespawnPoint(Player *plr) { return best; } +bool PlayerManager::isAccountInUse(int accountId) { + std::map::iterator it; + for (it = PlayerManager::players.begin(); it != PlayerManager::players.end(); it++) + { + if (it->second.plr->accountId == accountId) + return true; + } + return false; +} + +void PlayerManager::exitDuplicate(int accountId) { + std::map::iterator it; + for (it = PlayerManager::players.begin(); it != PlayerManager::players.end(); it++) + { + if (it->second.plr->accountId == accountId) + { + CNSocket* sock = it->first; + INITSTRUCT(sP_FE2CL_REP_PC_EXIT_DUPLICATE, resp); + resp.iErrorCode = 0; + sock->sendPacket((void*)&resp, P_FE2CL_REP_PC_EXIT_DUPLICATE, sizeof(sP_FE2CL_REP_PC_EXIT_DUPLICATE)); + sock->kill(); + } + } +} #pragma endregion diff --git a/src/PlayerManager.hpp b/src/PlayerManager.hpp index 52eb95a..0f46ef6 100644 --- a/src/PlayerManager.hpp +++ b/src/PlayerManager.hpp @@ -52,4 +52,7 @@ namespace PlayerManager { Player *getPlayer(CNSocket* key); WarpLocation getRespawnPoint(Player *plr); + + bool isAccountInUse(int accountId); + void exitDuplicate(int accountId); } diff --git a/src/contrib/sqlite/shell.c b/src/contrib/sqlite/shell.c deleted file mode 100644 index 9549bd6..0000000 --- a/src/contrib/sqlite/shell.c +++ /dev/null @@ -1,20842 +0,0 @@ -/* DO NOT EDIT! -** This file is automatically generated by the script in the canonical -** SQLite source tree at tool/mkshellc.tcl. That script combines source -** code from various constituent source files of SQLite into this single -** "shell.c" file used to implement the SQLite command-line shell. -** -** Most of the code found below comes from the "src/shell.c.in" file in -** the canonical SQLite source tree. That main file contains "INCLUDE" -** lines that specify other files in the canonical source tree that are -** inserted to getnerate this complete program source file. -** -** The code from multiple files is combined into this single "shell.c" -** source file to help make the command-line program easier to compile. -** -** To modify this program, get a copy of the canonical SQLite source tree, -** edit the src/shell.c.in" and/or some of the other files that are included -** by "src/shell.c.in", then rerun the tool/mkshellc.tcl script. -*/ -/* -** 2001 September 15 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** This file contains code to implement the "sqlite" command line -** utility for accessing SQLite databases. -*/ -#if (defined(_WIN32) || defined(WIN32)) && !defined(_CRT_SECURE_NO_WARNINGS) -/* This needs to come before any includes for MSVC compiler */ -#define _CRT_SECURE_NO_WARNINGS -#endif - -/* -** Determine if we are dealing with WinRT, which provides only a subset of -** the full Win32 API. -*/ -#if !defined(SQLITE_OS_WINRT) -# define SQLITE_OS_WINRT 0 -#endif - -/* -** Warning pragmas copied from msvc.h in the core. -*/ -#if defined(_MSC_VER) -#pragma warning(disable : 4054) -#pragma warning(disable : 4055) -#pragma warning(disable : 4100) -#pragma warning(disable : 4127) -#pragma warning(disable : 4130) -#pragma warning(disable : 4152) -#pragma warning(disable : 4189) -#pragma warning(disable : 4206) -#pragma warning(disable : 4210) -#pragma warning(disable : 4232) -#pragma warning(disable : 4244) -#pragma warning(disable : 4305) -#pragma warning(disable : 4306) -#pragma warning(disable : 4702) -#pragma warning(disable : 4706) -#endif /* defined(_MSC_VER) */ - -/* -** No support for loadable extensions in VxWorks. -*/ -#if (defined(__RTP__) || defined(_WRS_KERNEL)) && !SQLITE_OMIT_LOAD_EXTENSION -# define SQLITE_OMIT_LOAD_EXTENSION 1 -#endif - -/* -** Enable large-file support for fopen() and friends on unix. -*/ -#ifndef SQLITE_DISABLE_LFS -# define _LARGE_FILE 1 -# ifndef _FILE_OFFSET_BITS -# define _FILE_OFFSET_BITS 64 -# endif -# define _LARGEFILE_SOURCE 1 -#endif - -#include -#include -#include -#include -#include "sqlite3.h" -typedef sqlite3_int64 i64; -typedef sqlite3_uint64 u64; -typedef unsigned char u8; -#if SQLITE_USER_AUTHENTICATION -# include "sqlite3userauth.h" -#endif -#include -#include - -#if !defined(_WIN32) && !defined(WIN32) -# include -# if !defined(__RTP__) && !defined(_WRS_KERNEL) -# include -# endif -#endif -#if (!defined(_WIN32) && !defined(WIN32)) || defined(__MINGW32__) -# include -# include -# define GETPID getpid -# if defined(__MINGW32__) -# define DIRENT dirent -# ifndef S_ISLNK -# define S_ISLNK(mode) (0) -# endif -# endif -#else -# define GETPID (int)GetCurrentProcessId -#endif -#include -#include - -#if HAVE_READLINE -# include -# include -#endif - -#if HAVE_EDITLINE -# include -#endif - -#if HAVE_EDITLINE || HAVE_READLINE - -# define shell_add_history(X) add_history(X) -# define shell_read_history(X) read_history(X) -# define shell_write_history(X) write_history(X) -# define shell_stifle_history(X) stifle_history(X) -# define shell_readline(X) readline(X) - -#elif HAVE_LINENOISE - -# include "linenoise.h" -# define shell_add_history(X) linenoiseHistoryAdd(X) -# define shell_read_history(X) linenoiseHistoryLoad(X) -# define shell_write_history(X) linenoiseHistorySave(X) -# define shell_stifle_history(X) linenoiseHistorySetMaxLen(X) -# define shell_readline(X) linenoise(X) - -#else - -# define shell_read_history(X) -# define shell_write_history(X) -# define shell_stifle_history(X) - -# define SHELL_USE_LOCAL_GETLINE 1 -#endif - - -#if defined(_WIN32) || defined(WIN32) -# if SQLITE_OS_WINRT -# define SQLITE_OMIT_POPEN 1 -# else -# include -# include -# define isatty(h) _isatty(h) -# ifndef access -# define access(f,m) _access((f),(m)) -# endif -# ifndef unlink -# define unlink _unlink -# endif -# ifndef strdup -# define strdup _strdup -# endif -# undef popen -# define popen _popen -# undef pclose -# define pclose _pclose -# endif -#else - /* Make sure isatty() has a prototype. */ - extern int isatty(int); - -# if !defined(__RTP__) && !defined(_WRS_KERNEL) - /* popen and pclose are not C89 functions and so are - ** sometimes omitted from the header */ - extern FILE *popen(const char*,const char*); - extern int pclose(FILE*); -# else -# define SQLITE_OMIT_POPEN 1 -# endif -#endif - -#if defined(_WIN32_WCE) -/* Windows CE (arm-wince-mingw32ce-gcc) does not provide isatty() - * thus we always assume that we have a console. That can be - * overridden with the -batch command line option. - */ -#define isatty(x) 1 -#endif - -/* ctype macros that work with signed characters */ -#define IsSpace(X) isspace((unsigned char)X) -#define IsDigit(X) isdigit((unsigned char)X) -#define ToLower(X) (char)tolower((unsigned char)X) - -#if defined(_WIN32) || defined(WIN32) -#if SQLITE_OS_WINRT -#include -#endif -#include - -/* string conversion routines only needed on Win32 */ -extern char *sqlite3_win32_unicode_to_utf8(LPCWSTR); -extern char *sqlite3_win32_mbcs_to_utf8_v2(const char *, int); -extern char *sqlite3_win32_utf8_to_mbcs_v2(const char *, int); -extern LPWSTR sqlite3_win32_utf8_to_unicode(const char *zText); -#endif - -/* On Windows, we normally run with output mode of TEXT so that \n characters -** are automatically translated into \r\n. However, this behavior needs -** to be disabled in some cases (ex: when generating CSV output and when -** rendering quoted strings that contain \n characters). The following -** routines take care of that. -*/ -#if (defined(_WIN32) || defined(WIN32)) && !SQLITE_OS_WINRT -static void setBinaryMode(FILE *file, int isOutput){ - if( isOutput ) fflush(file); - _setmode(_fileno(file), _O_BINARY); -} -static void setTextMode(FILE *file, int isOutput){ - if( isOutput ) fflush(file); - _setmode(_fileno(file), _O_TEXT); -} -#else -# define setBinaryMode(X,Y) -# define setTextMode(X,Y) -#endif - - -/* True if the timer is enabled */ -static int enableTimer = 0; - -/* Return the current wall-clock time */ -static sqlite3_int64 timeOfDay(void){ - static sqlite3_vfs *clockVfs = 0; - sqlite3_int64 t; - if( clockVfs==0 ) clockVfs = sqlite3_vfs_find(0); - if( clockVfs->iVersion>=2 && clockVfs->xCurrentTimeInt64!=0 ){ - clockVfs->xCurrentTimeInt64(clockVfs, &t); - }else{ - double r; - clockVfs->xCurrentTime(clockVfs, &r); - t = (sqlite3_int64)(r*86400000.0); - } - return t; -} - -#if !defined(_WIN32) && !defined(WIN32) && !defined(__minux) -#include -#include - -/* VxWorks does not support getrusage() as far as we can determine */ -#if defined(_WRS_KERNEL) || defined(__RTP__) -struct rusage { - struct timeval ru_utime; /* - - - - CPU time used */ - struct timeval ru_stime; /* system CPU time used */ -}; -#define getrusage(A,B) memset(B,0,sizeof(*B)) -#endif - -/* Saved resource information for the beginning of an operation */ -static struct rusage sBegin; /* CPU time at start */ -static sqlite3_int64 iBegin; /* Wall-clock time at start */ - -/* -** Begin timing an operation -*/ -static void beginTimer(void){ - if( enableTimer ){ - getrusage(RUSAGE_SELF, &sBegin); - iBegin = timeOfDay(); - } -} - -/* Return the difference of two time_structs in seconds */ -static double timeDiff(struct timeval *pStart, struct timeval *pEnd){ - return (pEnd->tv_usec - pStart->tv_usec)*0.000001 + - (double)(pEnd->tv_sec - pStart->tv_sec); -} - -/* -** Print the timing results. -*/ -static void endTimer(void){ - if( enableTimer ){ - sqlite3_int64 iEnd = timeOfDay(); - struct rusage sEnd; - getrusage(RUSAGE_SELF, &sEnd); - printf("Run Time: real %.3f user %f sys %f\n", - (iEnd - iBegin)*0.001, - timeDiff(&sBegin.ru_utime, &sEnd.ru_utime), - timeDiff(&sBegin.ru_stime, &sEnd.ru_stime)); - } -} - -#define BEGIN_TIMER beginTimer() -#define END_TIMER endTimer() -#define HAS_TIMER 1 - -#elif (defined(_WIN32) || defined(WIN32)) - -/* Saved resource information for the beginning of an operation */ -static HANDLE hProcess; -static FILETIME ftKernelBegin; -static FILETIME ftUserBegin; -static sqlite3_int64 ftWallBegin; -typedef BOOL (WINAPI *GETPROCTIMES)(HANDLE, LPFILETIME, LPFILETIME, - LPFILETIME, LPFILETIME); -static GETPROCTIMES getProcessTimesAddr = NULL; - -/* -** Check to see if we have timer support. Return 1 if necessary -** support found (or found previously). -*/ -static int hasTimer(void){ - if( getProcessTimesAddr ){ - return 1; - } else { -#if !SQLITE_OS_WINRT - /* GetProcessTimes() isn't supported in WIN95 and some other Windows - ** versions. See if the version we are running on has it, and if it - ** does, save off a pointer to it and the current process handle. - */ - hProcess = GetCurrentProcess(); - if( hProcess ){ - HINSTANCE hinstLib = LoadLibrary(TEXT("Kernel32.dll")); - if( NULL != hinstLib ){ - getProcessTimesAddr = - (GETPROCTIMES) GetProcAddress(hinstLib, "GetProcessTimes"); - if( NULL != getProcessTimesAddr ){ - return 1; - } - FreeLibrary(hinstLib); - } - } -#endif - } - return 0; -} - -/* -** Begin timing an operation -*/ -static void beginTimer(void){ - if( enableTimer && getProcessTimesAddr ){ - FILETIME ftCreation, ftExit; - getProcessTimesAddr(hProcess,&ftCreation,&ftExit, - &ftKernelBegin,&ftUserBegin); - ftWallBegin = timeOfDay(); - } -} - -/* Return the difference of two FILETIME structs in seconds */ -static double timeDiff(FILETIME *pStart, FILETIME *pEnd){ - sqlite_int64 i64Start = *((sqlite_int64 *) pStart); - sqlite_int64 i64End = *((sqlite_int64 *) pEnd); - return (double) ((i64End - i64Start) / 10000000.0); -} - -/* -** Print the timing results. -*/ -static void endTimer(void){ - if( enableTimer && getProcessTimesAddr){ - FILETIME ftCreation, ftExit, ftKernelEnd, ftUserEnd; - sqlite3_int64 ftWallEnd = timeOfDay(); - getProcessTimesAddr(hProcess,&ftCreation,&ftExit,&ftKernelEnd,&ftUserEnd); - printf("Run Time: real %.3f user %f sys %f\n", - (ftWallEnd - ftWallBegin)*0.001, - timeDiff(&ftUserBegin, &ftUserEnd), - timeDiff(&ftKernelBegin, &ftKernelEnd)); - } -} - -#define BEGIN_TIMER beginTimer() -#define END_TIMER endTimer() -#define HAS_TIMER hasTimer() - -#else -#define BEGIN_TIMER -#define END_TIMER -#define HAS_TIMER 0 -#endif - -/* -** Used to prevent warnings about unused parameters -*/ -#define UNUSED_PARAMETER(x) (void)(x) - -/* -** Number of elements in an array -*/ -#define ArraySize(X) (int)(sizeof(X)/sizeof(X[0])) - -/* -** If the following flag is set, then command execution stops -** at an error if we are not interactive. -*/ -static int bail_on_error = 0; - -/* -** Threat stdin as an interactive input if the following variable -** is true. Otherwise, assume stdin is connected to a file or pipe. -*/ -static int stdin_is_interactive = 1; - -/* -** On Windows systems we have to know if standard output is a console -** in order to translate UTF-8 into MBCS. The following variable is -** true if translation is required. -*/ -static int stdout_is_console = 1; - -/* -** The following is the open SQLite database. We make a pointer -** to this database a static variable so that it can be accessed -** by the SIGINT handler to interrupt database processing. -*/ -static sqlite3 *globalDb = 0; - -/* -** True if an interrupt (Control-C) has been received. -*/ -static volatile int seenInterrupt = 0; - -#ifdef SQLITE_DEBUG -/* -** Out-of-memory simulator variables -*/ -static unsigned int oomCounter = 0; /* Simulate OOM when equals 1 */ -static unsigned int oomRepeat = 0; /* Number of OOMs in a row */ -static void*(*defaultMalloc)(int) = 0; /* The low-level malloc routine */ -#endif /* SQLITE_DEBUG */ - -/* -** This is the name of our program. It is set in main(), used -** in a number of other places, mostly for error messages. -*/ -static char *Argv0; - -/* -** Prompt strings. Initialized in main. Settable with -** .prompt main continue -*/ -static char mainPrompt[20]; /* First line prompt. default: "sqlite> "*/ -static char continuePrompt[20]; /* Continuation prompt. default: " ...> " */ - -/* -** Render output like fprintf(). Except, if the output is going to the -** console and if this is running on a Windows machine, translate the -** output from UTF-8 into MBCS. -*/ -#if defined(_WIN32) || defined(WIN32) -void utf8_printf(FILE *out, const char *zFormat, ...){ - va_list ap; - va_start(ap, zFormat); - if( stdout_is_console && (out==stdout || out==stderr) ){ - char *z1 = sqlite3_vmprintf(zFormat, ap); - char *z2 = sqlite3_win32_utf8_to_mbcs_v2(z1, 0); - sqlite3_free(z1); - fputs(z2, out); - sqlite3_free(z2); - }else{ - vfprintf(out, zFormat, ap); - } - va_end(ap); -} -#elif !defined(utf8_printf) -# define utf8_printf fprintf -#endif - -/* -** Render output like fprintf(). This should not be used on anything that -** includes string formatting (e.g. "%s"). -*/ -#if !defined(raw_printf) -# define raw_printf fprintf -#endif - -/* Indicate out-of-memory and exit. */ -static void shell_out_of_memory(void){ - raw_printf(stderr,"Error: out of memory\n"); - exit(1); -} - -#ifdef SQLITE_DEBUG -/* This routine is called when a simulated OOM occurs. It is broken -** out as a separate routine to make it easy to set a breakpoint on -** the OOM -*/ -void shellOomFault(void){ - if( oomRepeat>0 ){ - oomRepeat--; - }else{ - oomCounter--; - } -} -#endif /* SQLITE_DEBUG */ - -#ifdef SQLITE_DEBUG -/* This routine is a replacement malloc() that is used to simulate -** Out-Of-Memory (OOM) errors for testing purposes. -*/ -static void *oomMalloc(int nByte){ - if( oomCounter ){ - if( oomCounter==1 ){ - shellOomFault(); - return 0; - }else{ - oomCounter--; - } - } - return defaultMalloc(nByte); -} -#endif /* SQLITE_DEBUG */ - -#ifdef SQLITE_DEBUG -/* Register the OOM simulator. This must occur before any memory -** allocations */ -static void registerOomSimulator(void){ - sqlite3_mem_methods mem; - sqlite3_config(SQLITE_CONFIG_GETMALLOC, &mem); - defaultMalloc = mem.xMalloc; - mem.xMalloc = oomMalloc; - sqlite3_config(SQLITE_CONFIG_MALLOC, &mem); -} -#endif - -/* -** Write I/O traces to the following stream. -*/ -#ifdef SQLITE_ENABLE_IOTRACE -static FILE *iotrace = 0; -#endif - -/* -** This routine works like printf in that its first argument is a -** format string and subsequent arguments are values to be substituted -** in place of % fields. The result of formatting this string -** is written to iotrace. -*/ -#ifdef SQLITE_ENABLE_IOTRACE -static void SQLITE_CDECL iotracePrintf(const char *zFormat, ...){ - va_list ap; - char *z; - if( iotrace==0 ) return; - va_start(ap, zFormat); - z = sqlite3_vmprintf(zFormat, ap); - va_end(ap); - utf8_printf(iotrace, "%s", z); - sqlite3_free(z); -} -#endif - -/* -** Output string zUtf to stream pOut as w characters. If w is negative, -** then right-justify the text. W is the width in UTF-8 characters, not -** in bytes. This is different from the %*.*s specification in printf -** since with %*.*s the width is measured in bytes, not characters. -*/ -static void utf8_width_print(FILE *pOut, int w, const char *zUtf){ - int i; - int n; - int aw = w<0 ? -w : w; - char zBuf[1000]; - if( aw>(int)sizeof(zBuf)/3 ) aw = (int)sizeof(zBuf)/3; - for(i=n=0; zUtf[i]; i++){ - if( (zUtf[i]&0xc0)!=0x80 ){ - n++; - if( n==aw ){ - do{ i++; }while( (zUtf[i]&0xc0)==0x80 ); - break; - } - } - } - if( n>=aw ){ - utf8_printf(pOut, "%.*s", i, zUtf); - }else if( w<0 ){ - utf8_printf(pOut, "%*s%s", aw-n, "", zUtf); - }else{ - utf8_printf(pOut, "%s%*s", zUtf, aw-n, ""); - } -} - - -/* -** Determines if a string is a number of not. -*/ -static int isNumber(const char *z, int *realnum){ - if( *z=='-' || *z=='+' ) z++; - if( !IsDigit(*z) ){ - return 0; - } - z++; - if( realnum ) *realnum = 0; - while( IsDigit(*z) ){ z++; } - if( *z=='.' ){ - z++; - if( !IsDigit(*z) ) return 0; - while( IsDigit(*z) ){ z++; } - if( realnum ) *realnum = 1; - } - if( *z=='e' || *z=='E' ){ - z++; - if( *z=='+' || *z=='-' ) z++; - if( !IsDigit(*z) ) return 0; - while( IsDigit(*z) ){ z++; } - if( realnum ) *realnum = 1; - } - return *z==0; -} - -/* -** Compute a string length that is limited to what can be stored in -** lower 30 bits of a 32-bit signed integer. -*/ -static int strlen30(const char *z){ - const char *z2 = z; - while( *z2 ){ z2++; } - return 0x3fffffff & (int)(z2 - z); -} - -/* -** Return the length of a string in characters. Multibyte UTF8 characters -** count as a single character. -*/ -static int strlenChar(const char *z){ - int n = 0; - while( *z ){ - if( (0xc0&*(z++))!=0x80 ) n++; - } - return n; -} - -/* -** Return true if zFile does not exist or if it is not an ordinary file. -*/ -#ifdef _WIN32 -# define notNormalFile(X) 0 -#else -static int notNormalFile(const char *zFile){ - struct stat x; - int rc; - memset(&x, 0, sizeof(x)); - rc = stat(zFile, &x); - return rc || !S_ISREG(x.st_mode); -} -#endif - -/* -** This routine reads a line of text from FILE in, stores -** the text in memory obtained from malloc() and returns a pointer -** to the text. NULL is returned at end of file, or if malloc() -** fails. -** -** If zLine is not NULL then it is a malloced buffer returned from -** a previous call to this routine that may be reused. -*/ -static char *local_getline(char *zLine, FILE *in){ - int nLine = zLine==0 ? 0 : 100; - int n = 0; - - while( 1 ){ - if( n+100>nLine ){ - nLine = nLine*2 + 100; - zLine = realloc(zLine, nLine); - if( zLine==0 ) shell_out_of_memory(); - } - if( fgets(&zLine[n], nLine - n, in)==0 ){ - if( n==0 ){ - free(zLine); - return 0; - } - zLine[n] = 0; - break; - } - while( zLine[n] ) n++; - if( n>0 && zLine[n-1]=='\n' ){ - n--; - if( n>0 && zLine[n-1]=='\r' ) n--; - zLine[n] = 0; - break; - } - } -#if defined(_WIN32) || defined(WIN32) - /* For interactive input on Windows systems, translate the - ** multi-byte characterset characters into UTF-8. */ - if( stdin_is_interactive && in==stdin ){ - char *zTrans = sqlite3_win32_mbcs_to_utf8_v2(zLine, 0); - if( zTrans ){ - int nTrans = strlen30(zTrans)+1; - if( nTrans>nLine ){ - zLine = realloc(zLine, nTrans); - if( zLine==0 ) shell_out_of_memory(); - } - memcpy(zLine, zTrans, nTrans); - sqlite3_free(zTrans); - } - } -#endif /* defined(_WIN32) || defined(WIN32) */ - return zLine; -} - -/* -** Retrieve a single line of input text. -** -** If in==0 then read from standard input and prompt before each line. -** If isContinuation is true, then a continuation prompt is appropriate. -** If isContinuation is zero, then the main prompt should be used. -** -** If zPrior is not NULL then it is a buffer from a prior call to this -** routine that can be reused. -** -** The result is stored in space obtained from malloc() and must either -** be freed by the caller or else passed back into this routine via the -** zPrior argument for reuse. -*/ -static char *one_input_line(FILE *in, char *zPrior, int isContinuation){ - char *zPrompt; - char *zResult; - if( in!=0 ){ - zResult = local_getline(zPrior, in); - }else{ - zPrompt = isContinuation ? continuePrompt : mainPrompt; -#if SHELL_USE_LOCAL_GETLINE - printf("%s", zPrompt); - fflush(stdout); - zResult = local_getline(zPrior, stdin); -#else - free(zPrior); - zResult = shell_readline(zPrompt); - if( zResult && *zResult ) shell_add_history(zResult); -#endif - } - return zResult; -} - - -/* -** Return the value of a hexadecimal digit. Return -1 if the input -** is not a hex digit. -*/ -static int hexDigitValue(char c){ - if( c>='0' && c<='9' ) return c - '0'; - if( c>='a' && c<='f' ) return c - 'a' + 10; - if( c>='A' && c<='F' ) return c - 'A' + 10; - return -1; -} - -/* -** Interpret zArg as an integer value, possibly with suffixes. -*/ -static sqlite3_int64 integerValue(const char *zArg){ - sqlite3_int64 v = 0; - static const struct { char *zSuffix; int iMult; } aMult[] = { - { "KiB", 1024 }, - { "MiB", 1024*1024 }, - { "GiB", 1024*1024*1024 }, - { "KB", 1000 }, - { "MB", 1000000 }, - { "GB", 1000000000 }, - { "K", 1000 }, - { "M", 1000000 }, - { "G", 1000000000 }, - }; - int i; - int isNeg = 0; - if( zArg[0]=='-' ){ - isNeg = 1; - zArg++; - }else if( zArg[0]=='+' ){ - zArg++; - } - if( zArg[0]=='0' && zArg[1]=='x' ){ - int x; - zArg += 2; - while( (x = hexDigitValue(zArg[0]))>=0 ){ - v = (v<<4) + x; - zArg++; - } - }else{ - while( IsDigit(zArg[0]) ){ - v = v*10 + zArg[0] - '0'; - zArg++; - } - } - for(i=0; iz); - initText(p); -} - -/* 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 void appendText(ShellText *p, char const *zAppend, char quote){ - int len; - int i; - int nAppend = strlen30(zAppend); - - len = nAppend+p->n+1; - if( quote ){ - len += 2; - for(i=0; in+len>=p->nAlloc ){ - p->nAlloc = p->nAlloc*2 + len + 20; - p->z = realloc(p->z, p->nAlloc); - if( p->z==0 ) shell_out_of_memory(); - } - - if( quote ){ - char *zCsr = p->z+p->n; - *zCsr++ = quote; - for(i=0; in = (int)(zCsr - p->z); - *zCsr = '\0'; - }else{ - memcpy(p->z+p->n, zAppend, nAppend); - p->n += nAppend; - p->z[p->n] = '\0'; - } -} - -/* -** Attempt to determine if identifier zName needs to be quoted, either -** because it contains non-alphanumeric characters, or because it is an -** SQLite keyword. Be conservative in this estimate: When in doubt assume -** that quoting is required. -** -** Return '"' if quoting is required. Return 0 if no quoting is required. -*/ -static char quoteChar(const char *zName){ - int i; - if( !isalpha((unsigned char)zName[0]) && zName[0]!='_' ) return '"'; - for(i=0; zName[i]; i++){ - if( !isalnum((unsigned char)zName[i]) && zName[i]!='_' ) return '"'; - } - return sqlite3_keyword_check(zName, i) ? '"' : 0; -} - -/* -** Construct a fake object name and column list to describe the structure -** of the view, virtual table, or table valued function zSchema.zName. -*/ -static char *shellFakeSchema( - sqlite3 *db, /* The database connection containing the vtab */ - const char *zSchema, /* Schema of the database holding the vtab */ - const char *zName /* The name of the virtual table */ -){ - sqlite3_stmt *pStmt = 0; - char *zSql; - ShellText s; - char cQuote; - char *zDiv = "("; - int nRow = 0; - - zSql = sqlite3_mprintf("PRAGMA \"%w\".table_info=%Q;", - zSchema ? zSchema : "main", zName); - sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0); - sqlite3_free(zSql); - initText(&s); - if( zSchema ){ - cQuote = quoteChar(zSchema); - if( cQuote && sqlite3_stricmp(zSchema,"temp")==0 ) cQuote = 0; - appendText(&s, zSchema, cQuote); - appendText(&s, ".", 0); - } - cQuote = quoteChar(zName); - appendText(&s, zName, cQuote); - while( sqlite3_step(pStmt)==SQLITE_ROW ){ - const char *zCol = (const char*)sqlite3_column_text(pStmt, 1); - nRow++; - appendText(&s, zDiv, 0); - zDiv = ","; - cQuote = quoteChar(zCol); - appendText(&s, zCol, cQuote); - } - appendText(&s, ")", 0); - sqlite3_finalize(pStmt); - if( nRow==0 ){ - freeText(&s); - s.z = 0; - } - return s.z; -} - -/* -** SQL function: shell_module_schema(X) -** -** Return a fake schema for the table-valued function or eponymous virtual -** table X. -*/ -static void shellModuleSchema( - sqlite3_context *pCtx, - int nVal, - sqlite3_value **apVal -){ - const char *zName = (const char*)sqlite3_value_text(apVal[0]); - char *zFake = shellFakeSchema(sqlite3_context_db_handle(pCtx), 0, zName); - UNUSED_PARAMETER(nVal); - if( zFake ){ - sqlite3_result_text(pCtx, sqlite3_mprintf("/* %s */", zFake), - -1, sqlite3_free); - free(zFake); - } -} - -/* -** SQL function: shell_add_schema(S,X) -** -** Add the schema name X to the CREATE statement in S and return the result. -** Examples: -** -** CREATE TABLE t1(x) -> CREATE TABLE xyz.t1(x); -** -** Also works on -** -** CREATE INDEX -** CREATE UNIQUE INDEX -** CREATE VIEW -** CREATE TRIGGER -** CREATE VIRTUAL TABLE -** -** This UDF is used by the .schema command to insert the schema name of -** attached databases into the middle of the sqlite_schema.sql field. -*/ -static void shellAddSchemaName( - sqlite3_context *pCtx, - int nVal, - sqlite3_value **apVal -){ - static const char *aPrefix[] = { - "TABLE", - "INDEX", - "UNIQUE INDEX", - "VIEW", - "TRIGGER", - "VIRTUAL TABLE" - }; - int i = 0; - const char *zIn = (const char*)sqlite3_value_text(apVal[0]); - const char *zSchema = (const char*)sqlite3_value_text(apVal[1]); - const char *zName = (const char*)sqlite3_value_text(apVal[2]); - sqlite3 *db = sqlite3_context_db_handle(pCtx); - UNUSED_PARAMETER(nVal); - if( zIn!=0 && strncmp(zIn, "CREATE ", 7)==0 ){ - for(i=0; i<(int)(sizeof(aPrefix)/sizeof(aPrefix[0])); i++){ - int n = strlen30(aPrefix[i]); - if( strncmp(zIn+7, aPrefix[i], n)==0 && zIn[n+7]==' ' ){ - char *z = 0; - char *zFake = 0; - if( zSchema ){ - char cQuote = quoteChar(zSchema); - if( cQuote && sqlite3_stricmp(zSchema,"temp")!=0 ){ - z = sqlite3_mprintf("%.*s \"%w\".%s", n+7, zIn, zSchema, zIn+n+8); - }else{ - z = sqlite3_mprintf("%.*s %s.%s", n+7, zIn, zSchema, zIn+n+8); - } - } - if( zName - && aPrefix[i][0]=='V' - && (zFake = shellFakeSchema(db, zSchema, zName))!=0 - ){ - if( z==0 ){ - z = sqlite3_mprintf("%s\n/* %s */", zIn, zFake); - }else{ - z = sqlite3_mprintf("%z\n/* %s */", z, zFake); - } - free(zFake); - } - if( z ){ - sqlite3_result_text(pCtx, z, -1, sqlite3_free); - return; - } - } - } - } - sqlite3_result_value(pCtx, apVal[0]); -} - -/* -** The source code for several run-time loadable extensions is inserted -** below by the ../tool/mkshellc.tcl script. Before processing that included -** code, we need to override some macros to make the included program code -** work here in the middle of this regular program. -*/ -#define SQLITE_EXTENSION_INIT1 -#define SQLITE_EXTENSION_INIT2(X) (void)(X) - -#if defined(_WIN32) && defined(_MSC_VER) -/************************* Begin test_windirent.h ******************/ -/* -** 2015 November 30 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** This file contains declarations for most of the opendir() family of -** POSIX functions on Win32 using the MSVCRT. -*/ - -#if defined(_WIN32) && defined(_MSC_VER) && !defined(SQLITE_WINDIRENT_H) -#define SQLITE_WINDIRENT_H - -/* -** We need several data types from the Windows SDK header. -*/ - -#ifndef WIN32_LEAN_AND_MEAN -#define WIN32_LEAN_AND_MEAN -#endif - -#include "windows.h" - -/* -** We need several support functions from the SQLite core. -*/ - -/* #include "sqlite3.h" */ - -/* -** We need several things from the ANSI and MSVCRT headers. -*/ - -#include -#include -#include -#include -#include -#include -#include - -/* -** We may need several defines that should have been in "sys/stat.h". -*/ - -#ifndef S_ISREG -#define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG) -#endif - -#ifndef S_ISDIR -#define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR) -#endif - -#ifndef S_ISLNK -#define S_ISLNK(mode) (0) -#endif - -/* -** We may need to provide the "mode_t" type. -*/ - -#ifndef MODE_T_DEFINED - #define MODE_T_DEFINED - typedef unsigned short mode_t; -#endif - -/* -** We may need to provide the "ino_t" type. -*/ - -#ifndef INO_T_DEFINED - #define INO_T_DEFINED - typedef unsigned short ino_t; -#endif - -/* -** We need to define "NAME_MAX" if it was not present in "limits.h". -*/ - -#ifndef NAME_MAX -# ifdef FILENAME_MAX -# define NAME_MAX (FILENAME_MAX) -# else -# define NAME_MAX (260) -# endif -#endif - -/* -** We need to define "NULL_INTPTR_T" and "BAD_INTPTR_T". -*/ - -#ifndef NULL_INTPTR_T -# define NULL_INTPTR_T ((intptr_t)(0)) -#endif - -#ifndef BAD_INTPTR_T -# define BAD_INTPTR_T ((intptr_t)(-1)) -#endif - -/* -** We need to provide the necessary structures and related types. -*/ - -#ifndef DIRENT_DEFINED -#define DIRENT_DEFINED -typedef struct DIRENT DIRENT; -typedef DIRENT *LPDIRENT; -struct DIRENT { - ino_t d_ino; /* Sequence number, do not use. */ - unsigned d_attributes; /* Win32 file attributes. */ - char d_name[NAME_MAX + 1]; /* Name within the directory. */ -}; -#endif - -#ifndef DIR_DEFINED -#define DIR_DEFINED -typedef struct DIR DIR; -typedef DIR *LPDIR; -struct DIR { - intptr_t d_handle; /* Value returned by "_findfirst". */ - DIRENT d_first; /* DIRENT constructed based on "_findfirst". */ - DIRENT d_next; /* DIRENT constructed based on "_findnext". */ -}; -#endif - -/* -** Provide a macro, for use by the implementation, to determine if a -** particular directory entry should be skipped over when searching for -** the next directory entry that should be returned by the readdir() or -** readdir_r() functions. -*/ - -#ifndef is_filtered -# define is_filtered(a) ((((a).attrib)&_A_HIDDEN) || (((a).attrib)&_A_SYSTEM)) -#endif - -/* -** Provide the function prototype for the POSIX compatiable getenv() -** function. This function is not thread-safe. -*/ - -extern const char *windirent_getenv(const char *name); - -/* -** Finally, we can provide the function prototypes for the opendir(), -** readdir(), readdir_r(), and closedir() POSIX functions. -*/ - -extern LPDIR opendir(const char *dirname); -extern LPDIRENT readdir(LPDIR dirp); -extern INT readdir_r(LPDIR dirp, LPDIRENT entry, LPDIRENT *result); -extern INT closedir(LPDIR dirp); - -#endif /* defined(WIN32) && defined(_MSC_VER) */ - -/************************* End test_windirent.h ********************/ -/************************* Begin test_windirent.c ******************/ -/* -** 2015 November 30 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** This file contains code to implement most of the opendir() family of -** POSIX functions on Win32 using the MSVCRT. -*/ - -#if defined(_WIN32) && defined(_MSC_VER) -/* #include "test_windirent.h" */ - -/* -** Implementation of the POSIX getenv() function using the Win32 API. -** This function is not thread-safe. -*/ -const char *windirent_getenv( - const char *name -){ - static char value[32768]; /* Maximum length, per MSDN */ - DWORD dwSize = sizeof(value) / sizeof(char); /* Size in chars */ - DWORD dwRet; /* Value returned by GetEnvironmentVariableA() */ - - memset(value, 0, sizeof(value)); - dwRet = GetEnvironmentVariableA(name, value, dwSize); - if( dwRet==0 || dwRet>dwSize ){ - /* - ** The function call to GetEnvironmentVariableA() failed -OR- - ** the buffer is not large enough. Either way, return NULL. - */ - return 0; - }else{ - /* - ** The function call to GetEnvironmentVariableA() succeeded - ** -AND- the buffer contains the entire value. - */ - return value; - } -} - -/* -** Implementation of the POSIX opendir() function using the MSVCRT. -*/ -LPDIR opendir( - const char *dirname -){ - struct _finddata_t data; - LPDIR dirp = (LPDIR)sqlite3_malloc(sizeof(DIR)); - SIZE_T namesize = sizeof(data.name) / sizeof(data.name[0]); - - if( dirp==NULL ) return NULL; - memset(dirp, 0, sizeof(DIR)); - - /* TODO: Remove this if Unix-style root paths are not used. */ - if( sqlite3_stricmp(dirname, "/")==0 ){ - dirname = windirent_getenv("SystemDrive"); - } - - memset(&data, 0, sizeof(struct _finddata_t)); - _snprintf(data.name, namesize, "%s\\*", dirname); - dirp->d_handle = _findfirst(data.name, &data); - - if( dirp->d_handle==BAD_INTPTR_T ){ - closedir(dirp); - return NULL; - } - - /* TODO: Remove this block to allow hidden and/or system files. */ - if( is_filtered(data) ){ -next: - - memset(&data, 0, sizeof(struct _finddata_t)); - if( _findnext(dirp->d_handle, &data)==-1 ){ - closedir(dirp); - return NULL; - } - - /* TODO: Remove this block to allow hidden and/or system files. */ - if( is_filtered(data) ) goto next; - } - - dirp->d_first.d_attributes = data.attrib; - strncpy(dirp->d_first.d_name, data.name, NAME_MAX); - dirp->d_first.d_name[NAME_MAX] = '\0'; - - return dirp; -} - -/* -** Implementation of the POSIX readdir() function using the MSVCRT. -*/ -LPDIRENT readdir( - LPDIR dirp -){ - struct _finddata_t data; - - if( dirp==NULL ) return NULL; - - if( dirp->d_first.d_ino==0 ){ - dirp->d_first.d_ino++; - dirp->d_next.d_ino++; - - return &dirp->d_first; - } - -next: - - memset(&data, 0, sizeof(struct _finddata_t)); - if( _findnext(dirp->d_handle, &data)==-1 ) return NULL; - - /* TODO: Remove this block to allow hidden and/or system files. */ - if( is_filtered(data) ) goto next; - - dirp->d_next.d_ino++; - dirp->d_next.d_attributes = data.attrib; - strncpy(dirp->d_next.d_name, data.name, NAME_MAX); - dirp->d_next.d_name[NAME_MAX] = '\0'; - - return &dirp->d_next; -} - -/* -** Implementation of the POSIX readdir_r() function using the MSVCRT. -*/ -INT readdir_r( - LPDIR dirp, - LPDIRENT entry, - LPDIRENT *result -){ - struct _finddata_t data; - - if( dirp==NULL ) return EBADF; - - if( dirp->d_first.d_ino==0 ){ - dirp->d_first.d_ino++; - dirp->d_next.d_ino++; - - entry->d_ino = dirp->d_first.d_ino; - entry->d_attributes = dirp->d_first.d_attributes; - strncpy(entry->d_name, dirp->d_first.d_name, NAME_MAX); - entry->d_name[NAME_MAX] = '\0'; - - *result = entry; - return 0; - } - -next: - - memset(&data, 0, sizeof(struct _finddata_t)); - if( _findnext(dirp->d_handle, &data)==-1 ){ - *result = NULL; - return ENOENT; - } - - /* TODO: Remove this block to allow hidden and/or system files. */ - if( is_filtered(data) ) goto next; - - entry->d_ino = (ino_t)-1; /* not available */ - entry->d_attributes = data.attrib; - strncpy(entry->d_name, data.name, NAME_MAX); - entry->d_name[NAME_MAX] = '\0'; - - *result = entry; - return 0; -} - -/* -** Implementation of the POSIX closedir() function using the MSVCRT. -*/ -INT closedir( - LPDIR dirp -){ - INT result = 0; - - if( dirp==NULL ) return EINVAL; - - if( dirp->d_handle!=NULL_INTPTR_T && dirp->d_handle!=BAD_INTPTR_T ){ - result = _findclose(dirp->d_handle); - } - - sqlite3_free(dirp); - return result; -} - -#endif /* defined(WIN32) && defined(_MSC_VER) */ - -/************************* End test_windirent.c ********************/ -#define dirent DIRENT -#endif -/************************* Begin ../ext/misc/shathree.c ******************/ -/* -** 2017-03-08 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -****************************************************************************** -** -** This SQLite extension implements functions that compute SHA3 hashes. -** Two SQL functions are implemented: -** -** sha3(X,SIZE) -** sha3_query(Y,SIZE) -** -** The sha3(X) function computes the SHA3 hash of the input X, or NULL if -** X is NULL. -** -** The sha3_query(Y) function evalutes all queries in the SQL statements of Y -** and returns a hash of their results. -** -** The SIZE argument is optional. If omitted, the SHA3-256 hash algorithm -** is used. If SIZE is included it must be one of the integers 224, 256, -** 384, or 512, to determine SHA3 hash variant that is computed. -*/ -/* #include "sqlite3ext.h" */ -SQLITE_EXTENSION_INIT1 -#include -#include -#include -/* typedef sqlite3_uint64 u64; */ - -/****************************************************************************** -** The Hash Engine -*/ -/* -** Macros to determine whether the machine is big or little endian, -** and whether or not that determination is run-time or compile-time. -** -** For best performance, an attempt is made to guess at the byte-order -** using C-preprocessor macros. If that is unsuccessful, or if -** -DSHA3_BYTEORDER=0 is set, then byte-order is determined -** at run-time. -*/ -#ifndef SHA3_BYTEORDER -# if defined(i386) || defined(__i386__) || defined(_M_IX86) || \ - defined(__x86_64) || defined(__x86_64__) || defined(_M_X64) || \ - defined(_M_AMD64) || defined(_M_ARM) || defined(__x86) || \ - defined(__arm__) -# define SHA3_BYTEORDER 1234 -# elif defined(sparc) || defined(__ppc__) -# define SHA3_BYTEORDER 4321 -# else -# define SHA3_BYTEORDER 0 -# endif -#endif - - -/* -** State structure for a SHA3 hash in progress -*/ -typedef struct SHA3Context SHA3Context; -struct SHA3Context { - union { - u64 s[25]; /* Keccak state. 5x5 lines of 64 bits each */ - unsigned char x[1600]; /* ... or 1600 bytes */ - } u; - unsigned nRate; /* Bytes of input accepted per Keccak iteration */ - unsigned nLoaded; /* Input bytes loaded into u.x[] so far this cycle */ - unsigned ixMask; /* Insert next input into u.x[nLoaded^ixMask]. */ -}; - -/* -** A single step of the Keccak mixing function for a 1600-bit state -*/ -static void KeccakF1600Step(SHA3Context *p){ - int i; - u64 b0, b1, b2, b3, b4; - u64 c0, c1, c2, c3, c4; - u64 d0, d1, d2, d3, d4; - static const u64 RC[] = { - 0x0000000000000001ULL, 0x0000000000008082ULL, - 0x800000000000808aULL, 0x8000000080008000ULL, - 0x000000000000808bULL, 0x0000000080000001ULL, - 0x8000000080008081ULL, 0x8000000000008009ULL, - 0x000000000000008aULL, 0x0000000000000088ULL, - 0x0000000080008009ULL, 0x000000008000000aULL, - 0x000000008000808bULL, 0x800000000000008bULL, - 0x8000000000008089ULL, 0x8000000000008003ULL, - 0x8000000000008002ULL, 0x8000000000000080ULL, - 0x000000000000800aULL, 0x800000008000000aULL, - 0x8000000080008081ULL, 0x8000000000008080ULL, - 0x0000000080000001ULL, 0x8000000080008008ULL - }; -# define a00 (p->u.s[0]) -# define a01 (p->u.s[1]) -# define a02 (p->u.s[2]) -# define a03 (p->u.s[3]) -# define a04 (p->u.s[4]) -# define a10 (p->u.s[5]) -# define a11 (p->u.s[6]) -# define a12 (p->u.s[7]) -# define a13 (p->u.s[8]) -# define a14 (p->u.s[9]) -# define a20 (p->u.s[10]) -# define a21 (p->u.s[11]) -# define a22 (p->u.s[12]) -# define a23 (p->u.s[13]) -# define a24 (p->u.s[14]) -# define a30 (p->u.s[15]) -# define a31 (p->u.s[16]) -# define a32 (p->u.s[17]) -# define a33 (p->u.s[18]) -# define a34 (p->u.s[19]) -# define a40 (p->u.s[20]) -# define a41 (p->u.s[21]) -# define a42 (p->u.s[22]) -# define a43 (p->u.s[23]) -# define a44 (p->u.s[24]) -# define ROL64(a,x) ((a<>(64-x))) - - for(i=0; i<24; i+=4){ - c0 = a00^a10^a20^a30^a40; - c1 = a01^a11^a21^a31^a41; - c2 = a02^a12^a22^a32^a42; - c3 = a03^a13^a23^a33^a43; - c4 = a04^a14^a24^a34^a44; - d0 = c4^ROL64(c1, 1); - d1 = c0^ROL64(c2, 1); - d2 = c1^ROL64(c3, 1); - d3 = c2^ROL64(c4, 1); - d4 = c3^ROL64(c0, 1); - - b0 = (a00^d0); - b1 = ROL64((a11^d1), 44); - b2 = ROL64((a22^d2), 43); - b3 = ROL64((a33^d3), 21); - b4 = ROL64((a44^d4), 14); - a00 = b0 ^((~b1)& b2 ); - a00 ^= RC[i]; - a11 = b1 ^((~b2)& b3 ); - a22 = b2 ^((~b3)& b4 ); - a33 = b3 ^((~b4)& b0 ); - a44 = b4 ^((~b0)& b1 ); - - b2 = ROL64((a20^d0), 3); - b3 = ROL64((a31^d1), 45); - b4 = ROL64((a42^d2), 61); - b0 = ROL64((a03^d3), 28); - b1 = ROL64((a14^d4), 20); - a20 = b0 ^((~b1)& b2 ); - a31 = b1 ^((~b2)& b3 ); - a42 = b2 ^((~b3)& b4 ); - a03 = b3 ^((~b4)& b0 ); - a14 = b4 ^((~b0)& b1 ); - - b4 = ROL64((a40^d0), 18); - b0 = ROL64((a01^d1), 1); - b1 = ROL64((a12^d2), 6); - b2 = ROL64((a23^d3), 25); - b3 = ROL64((a34^d4), 8); - a40 = b0 ^((~b1)& b2 ); - a01 = b1 ^((~b2)& b3 ); - a12 = b2 ^((~b3)& b4 ); - a23 = b3 ^((~b4)& b0 ); - a34 = b4 ^((~b0)& b1 ); - - b1 = ROL64((a10^d0), 36); - b2 = ROL64((a21^d1), 10); - b3 = ROL64((a32^d2), 15); - b4 = ROL64((a43^d3), 56); - b0 = ROL64((a04^d4), 27); - a10 = b0 ^((~b1)& b2 ); - a21 = b1 ^((~b2)& b3 ); - a32 = b2 ^((~b3)& b4 ); - a43 = b3 ^((~b4)& b0 ); - a04 = b4 ^((~b0)& b1 ); - - b3 = ROL64((a30^d0), 41); - b4 = ROL64((a41^d1), 2); - b0 = ROL64((a02^d2), 62); - b1 = ROL64((a13^d3), 55); - b2 = ROL64((a24^d4), 39); - a30 = b0 ^((~b1)& b2 ); - a41 = b1 ^((~b2)& b3 ); - a02 = b2 ^((~b3)& b4 ); - a13 = b3 ^((~b4)& b0 ); - a24 = b4 ^((~b0)& b1 ); - - c0 = a00^a20^a40^a10^a30; - c1 = a11^a31^a01^a21^a41; - c2 = a22^a42^a12^a32^a02; - c3 = a33^a03^a23^a43^a13; - c4 = a44^a14^a34^a04^a24; - d0 = c4^ROL64(c1, 1); - d1 = c0^ROL64(c2, 1); - d2 = c1^ROL64(c3, 1); - d3 = c2^ROL64(c4, 1); - d4 = c3^ROL64(c0, 1); - - b0 = (a00^d0); - b1 = ROL64((a31^d1), 44); - b2 = ROL64((a12^d2), 43); - b3 = ROL64((a43^d3), 21); - b4 = ROL64((a24^d4), 14); - a00 = b0 ^((~b1)& b2 ); - a00 ^= RC[i+1]; - a31 = b1 ^((~b2)& b3 ); - a12 = b2 ^((~b3)& b4 ); - a43 = b3 ^((~b4)& b0 ); - a24 = b4 ^((~b0)& b1 ); - - b2 = ROL64((a40^d0), 3); - b3 = ROL64((a21^d1), 45); - b4 = ROL64((a02^d2), 61); - b0 = ROL64((a33^d3), 28); - b1 = ROL64((a14^d4), 20); - a40 = b0 ^((~b1)& b2 ); - a21 = b1 ^((~b2)& b3 ); - a02 = b2 ^((~b3)& b4 ); - a33 = b3 ^((~b4)& b0 ); - a14 = b4 ^((~b0)& b1 ); - - b4 = ROL64((a30^d0), 18); - b0 = ROL64((a11^d1), 1); - b1 = ROL64((a42^d2), 6); - b2 = ROL64((a23^d3), 25); - b3 = ROL64((a04^d4), 8); - a30 = b0 ^((~b1)& b2 ); - a11 = b1 ^((~b2)& b3 ); - a42 = b2 ^((~b3)& b4 ); - a23 = b3 ^((~b4)& b0 ); - a04 = b4 ^((~b0)& b1 ); - - b1 = ROL64((a20^d0), 36); - b2 = ROL64((a01^d1), 10); - b3 = ROL64((a32^d2), 15); - b4 = ROL64((a13^d3), 56); - b0 = ROL64((a44^d4), 27); - a20 = b0 ^((~b1)& b2 ); - a01 = b1 ^((~b2)& b3 ); - a32 = b2 ^((~b3)& b4 ); - a13 = b3 ^((~b4)& b0 ); - a44 = b4 ^((~b0)& b1 ); - - b3 = ROL64((a10^d0), 41); - b4 = ROL64((a41^d1), 2); - b0 = ROL64((a22^d2), 62); - b1 = ROL64((a03^d3), 55); - b2 = ROL64((a34^d4), 39); - a10 = b0 ^((~b1)& b2 ); - a41 = b1 ^((~b2)& b3 ); - a22 = b2 ^((~b3)& b4 ); - a03 = b3 ^((~b4)& b0 ); - a34 = b4 ^((~b0)& b1 ); - - c0 = a00^a40^a30^a20^a10; - c1 = a31^a21^a11^a01^a41; - c2 = a12^a02^a42^a32^a22; - c3 = a43^a33^a23^a13^a03; - c4 = a24^a14^a04^a44^a34; - d0 = c4^ROL64(c1, 1); - d1 = c0^ROL64(c2, 1); - d2 = c1^ROL64(c3, 1); - d3 = c2^ROL64(c4, 1); - d4 = c3^ROL64(c0, 1); - - b0 = (a00^d0); - b1 = ROL64((a21^d1), 44); - b2 = ROL64((a42^d2), 43); - b3 = ROL64((a13^d3), 21); - b4 = ROL64((a34^d4), 14); - a00 = b0 ^((~b1)& b2 ); - a00 ^= RC[i+2]; - a21 = b1 ^((~b2)& b3 ); - a42 = b2 ^((~b3)& b4 ); - a13 = b3 ^((~b4)& b0 ); - a34 = b4 ^((~b0)& b1 ); - - b2 = ROL64((a30^d0), 3); - b3 = ROL64((a01^d1), 45); - b4 = ROL64((a22^d2), 61); - b0 = ROL64((a43^d3), 28); - b1 = ROL64((a14^d4), 20); - a30 = b0 ^((~b1)& b2 ); - a01 = b1 ^((~b2)& b3 ); - a22 = b2 ^((~b3)& b4 ); - a43 = b3 ^((~b4)& b0 ); - a14 = b4 ^((~b0)& b1 ); - - b4 = ROL64((a10^d0), 18); - b0 = ROL64((a31^d1), 1); - b1 = ROL64((a02^d2), 6); - b2 = ROL64((a23^d3), 25); - b3 = ROL64((a44^d4), 8); - a10 = b0 ^((~b1)& b2 ); - a31 = b1 ^((~b2)& b3 ); - a02 = b2 ^((~b3)& b4 ); - a23 = b3 ^((~b4)& b0 ); - a44 = b4 ^((~b0)& b1 ); - - b1 = ROL64((a40^d0), 36); - b2 = ROL64((a11^d1), 10); - b3 = ROL64((a32^d2), 15); - b4 = ROL64((a03^d3), 56); - b0 = ROL64((a24^d4), 27); - a40 = b0 ^((~b1)& b2 ); - a11 = b1 ^((~b2)& b3 ); - a32 = b2 ^((~b3)& b4 ); - a03 = b3 ^((~b4)& b0 ); - a24 = b4 ^((~b0)& b1 ); - - b3 = ROL64((a20^d0), 41); - b4 = ROL64((a41^d1), 2); - b0 = ROL64((a12^d2), 62); - b1 = ROL64((a33^d3), 55); - b2 = ROL64((a04^d4), 39); - a20 = b0 ^((~b1)& b2 ); - a41 = b1 ^((~b2)& b3 ); - a12 = b2 ^((~b3)& b4 ); - a33 = b3 ^((~b4)& b0 ); - a04 = b4 ^((~b0)& b1 ); - - c0 = a00^a30^a10^a40^a20; - c1 = a21^a01^a31^a11^a41; - c2 = a42^a22^a02^a32^a12; - c3 = a13^a43^a23^a03^a33; - c4 = a34^a14^a44^a24^a04; - d0 = c4^ROL64(c1, 1); - d1 = c0^ROL64(c2, 1); - d2 = c1^ROL64(c3, 1); - d3 = c2^ROL64(c4, 1); - d4 = c3^ROL64(c0, 1); - - b0 = (a00^d0); - b1 = ROL64((a01^d1), 44); - b2 = ROL64((a02^d2), 43); - b3 = ROL64((a03^d3), 21); - b4 = ROL64((a04^d4), 14); - a00 = b0 ^((~b1)& b2 ); - a00 ^= RC[i+3]; - a01 = b1 ^((~b2)& b3 ); - a02 = b2 ^((~b3)& b4 ); - a03 = b3 ^((~b4)& b0 ); - a04 = b4 ^((~b0)& b1 ); - - b2 = ROL64((a10^d0), 3); - b3 = ROL64((a11^d1), 45); - b4 = ROL64((a12^d2), 61); - b0 = ROL64((a13^d3), 28); - b1 = ROL64((a14^d4), 20); - a10 = b0 ^((~b1)& b2 ); - a11 = b1 ^((~b2)& b3 ); - a12 = b2 ^((~b3)& b4 ); - a13 = b3 ^((~b4)& b0 ); - a14 = b4 ^((~b0)& b1 ); - - b4 = ROL64((a20^d0), 18); - b0 = ROL64((a21^d1), 1); - b1 = ROL64((a22^d2), 6); - b2 = ROL64((a23^d3), 25); - b3 = ROL64((a24^d4), 8); - a20 = b0 ^((~b1)& b2 ); - a21 = b1 ^((~b2)& b3 ); - a22 = b2 ^((~b3)& b4 ); - a23 = b3 ^((~b4)& b0 ); - a24 = b4 ^((~b0)& b1 ); - - b1 = ROL64((a30^d0), 36); - b2 = ROL64((a31^d1), 10); - b3 = ROL64((a32^d2), 15); - b4 = ROL64((a33^d3), 56); - b0 = ROL64((a34^d4), 27); - a30 = b0 ^((~b1)& b2 ); - a31 = b1 ^((~b2)& b3 ); - a32 = b2 ^((~b3)& b4 ); - a33 = b3 ^((~b4)& b0 ); - a34 = b4 ^((~b0)& b1 ); - - b3 = ROL64((a40^d0), 41); - b4 = ROL64((a41^d1), 2); - b0 = ROL64((a42^d2), 62); - b1 = ROL64((a43^d3), 55); - b2 = ROL64((a44^d4), 39); - a40 = b0 ^((~b1)& b2 ); - a41 = b1 ^((~b2)& b3 ); - a42 = b2 ^((~b3)& b4 ); - a43 = b3 ^((~b4)& b0 ); - a44 = b4 ^((~b0)& b1 ); - } -} - -/* -** Initialize a new hash. iSize determines the size of the hash -** in bits and should be one of 224, 256, 384, or 512. Or iSize -** can be zero to use the default hash size of 256 bits. -*/ -static void SHA3Init(SHA3Context *p, int iSize){ - memset(p, 0, sizeof(*p)); - if( iSize>=128 && iSize<=512 ){ - p->nRate = (1600 - ((iSize + 31)&~31)*2)/8; - }else{ - p->nRate = (1600 - 2*256)/8; - } -#if SHA3_BYTEORDER==1234 - /* Known to be little-endian at compile-time. No-op */ -#elif SHA3_BYTEORDER==4321 - p->ixMask = 7; /* Big-endian */ -#else - { - static unsigned int one = 1; - if( 1==*(unsigned char*)&one ){ - /* Little endian. No byte swapping. */ - p->ixMask = 0; - }else{ - /* Big endian. Byte swap. */ - p->ixMask = 7; - } - } -#endif -} - -/* -** Make consecutive calls to the SHA3Update function to add new content -** to the hash -*/ -static void SHA3Update( - SHA3Context *p, - const unsigned char *aData, - unsigned int nData -){ - unsigned int i = 0; -#if SHA3_BYTEORDER==1234 - if( (p->nLoaded % 8)==0 && ((aData - (const unsigned char*)0)&7)==0 ){ - for(; i+7u.s[p->nLoaded/8] ^= *(u64*)&aData[i]; - p->nLoaded += 8; - if( p->nLoaded>=p->nRate ){ - KeccakF1600Step(p); - p->nLoaded = 0; - } - } - } -#endif - for(; iu.x[p->nLoaded] ^= aData[i]; -#elif SHA3_BYTEORDER==4321 - p->u.x[p->nLoaded^0x07] ^= aData[i]; -#else - p->u.x[p->nLoaded^p->ixMask] ^= aData[i]; -#endif - p->nLoaded++; - if( p->nLoaded==p->nRate ){ - KeccakF1600Step(p); - p->nLoaded = 0; - } - } -} - -/* -** After all content has been added, invoke SHA3Final() to compute -** the final hash. The function returns a pointer to the binary -** hash value. -*/ -static unsigned char *SHA3Final(SHA3Context *p){ - unsigned int i; - if( p->nLoaded==p->nRate-1 ){ - const unsigned char c1 = 0x86; - SHA3Update(p, &c1, 1); - }else{ - const unsigned char c2 = 0x06; - const unsigned char c3 = 0x80; - SHA3Update(p, &c2, 1); - p->nLoaded = p->nRate - 1; - SHA3Update(p, &c3, 1); - } - for(i=0; inRate; i++){ - p->u.x[i+p->nRate] = p->u.x[i^p->ixMask]; - } - return &p->u.x[p->nRate]; -} -/* End of the hashing logic -*****************************************************************************/ - -/* -** Implementation of the sha3(X,SIZE) function. -** -** Return a BLOB which is the SIZE-bit SHA3 hash of X. The default -** size is 256. If X is a BLOB, it is hashed as is. -** For all other non-NULL types of input, X is converted into a UTF-8 string -** and the string is hashed without the trailing 0x00 terminator. The hash -** of a NULL value is NULL. -*/ -static void sha3Func( - sqlite3_context *context, - int argc, - sqlite3_value **argv -){ - SHA3Context cx; - int eType = sqlite3_value_type(argv[0]); - int nByte = sqlite3_value_bytes(argv[0]); - int iSize; - if( argc==1 ){ - iSize = 256; - }else{ - iSize = sqlite3_value_int(argv[1]); - if( iSize!=224 && iSize!=256 && iSize!=384 && iSize!=512 ){ - sqlite3_result_error(context, "SHA3 size should be one of: 224 256 " - "384 512", -1); - return; - } - } - if( eType==SQLITE_NULL ) return; - SHA3Init(&cx, iSize); - if( eType==SQLITE_BLOB ){ - SHA3Update(&cx, sqlite3_value_blob(argv[0]), nByte); - }else{ - SHA3Update(&cx, sqlite3_value_text(argv[0]), nByte); - } - sqlite3_result_blob(context, SHA3Final(&cx), iSize/8, SQLITE_TRANSIENT); -} - -/* Compute a string using sqlite3_vsnprintf() with a maximum length -** of 50 bytes and add it to the hash. -*/ -static void hash_step_vformat( - SHA3Context *p, /* Add content to this context */ - const char *zFormat, - ... -){ - va_list ap; - int n; - char zBuf[50]; - va_start(ap, zFormat); - sqlite3_vsnprintf(sizeof(zBuf),zBuf,zFormat,ap); - va_end(ap); - n = (int)strlen(zBuf); - SHA3Update(p, (unsigned char*)zBuf, n); -} - -/* -** Implementation of the sha3_query(SQL,SIZE) function. -** -** This function compiles and runs the SQL statement(s) given in the -** argument. The results are hashed using a SIZE-bit SHA3. The default -** size is 256. -** -** The format of the byte stream that is hashed is summarized as follows: -** -** S: -** R -** N -** I -** F -** B: -** T: -** -** is the original SQL text for each statement run and is -** the size of that text. The SQL text is UTF-8. A single R character -** occurs before the start of each row. N means a NULL value. -** I mean an 8-byte little-endian integer . F is a floating point -** number with an 8-byte little-endian IEEE floating point value . -** B means blobs of bytes. T means text rendered as -** bytes of UTF-8. The and values are expressed as an ASCII -** text integers. -** -** For each SQL statement in the X input, there is one S segment. Each -** S segment is followed by zero or more R segments, one for each row in the -** result set. After each R, there are one or more N, I, F, B, or T segments, -** one for each column in the result set. Segments are concatentated directly -** with no delimiters of any kind. -*/ -static void sha3QueryFunc( - sqlite3_context *context, - int argc, - sqlite3_value **argv -){ - sqlite3 *db = sqlite3_context_db_handle(context); - const char *zSql = (const char*)sqlite3_value_text(argv[0]); - sqlite3_stmt *pStmt = 0; - int nCol; /* Number of columns in the result set */ - int i; /* Loop counter */ - int rc; - int n; - const char *z; - SHA3Context cx; - int iSize; - - if( argc==1 ){ - iSize = 256; - }else{ - iSize = sqlite3_value_int(argv[1]); - if( iSize!=224 && iSize!=256 && iSize!=384 && iSize!=512 ){ - sqlite3_result_error(context, "SHA3 size should be one of: 224 256 " - "384 512", -1); - return; - } - } - if( zSql==0 ) return; - SHA3Init(&cx, iSize); - while( zSql[0] ){ - rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, &zSql); - if( rc ){ - char *zMsg = sqlite3_mprintf("error SQL statement [%s]: %s", - zSql, sqlite3_errmsg(db)); - sqlite3_finalize(pStmt); - sqlite3_result_error(context, zMsg, -1); - sqlite3_free(zMsg); - return; - } - if( !sqlite3_stmt_readonly(pStmt) ){ - char *zMsg = sqlite3_mprintf("non-query: [%s]", sqlite3_sql(pStmt)); - sqlite3_finalize(pStmt); - sqlite3_result_error(context, zMsg, -1); - sqlite3_free(zMsg); - return; - } - nCol = sqlite3_column_count(pStmt); - z = sqlite3_sql(pStmt); - n = (int)strlen(z); - hash_step_vformat(&cx,"S%d:",n); - SHA3Update(&cx,(unsigned char*)z,n); - - /* Compute a hash over the result of the query */ - while( SQLITE_ROW==sqlite3_step(pStmt) ){ - SHA3Update(&cx,(const unsigned char*)"R",1); - for(i=0; i=1; j--){ - x[j] = u & 0xff; - u >>= 8; - } - x[0] = 'I'; - SHA3Update(&cx, x, 9); - break; - } - case SQLITE_FLOAT: { - sqlite3_uint64 u; - int j; - unsigned char x[9]; - double r = sqlite3_column_double(pStmt,i); - memcpy(&u, &r, 8); - for(j=8; j>=1; j--){ - x[j] = u & 0xff; - u >>= 8; - } - x[0] = 'F'; - SHA3Update(&cx,x,9); - break; - } - case SQLITE_TEXT: { - int n2 = sqlite3_column_bytes(pStmt, i); - const unsigned char *z2 = sqlite3_column_text(pStmt, i); - hash_step_vformat(&cx,"T%d:",n2); - SHA3Update(&cx, z2, n2); - break; - } - case SQLITE_BLOB: { - int n2 = sqlite3_column_bytes(pStmt, i); - const unsigned char *z2 = sqlite3_column_blob(pStmt, i); - hash_step_vformat(&cx,"B%d:",n2); - SHA3Update(&cx, z2, n2); - break; - } - } - } - } - sqlite3_finalize(pStmt); - } - sqlite3_result_blob(context, SHA3Final(&cx), iSize/8, SQLITE_TRANSIENT); -} - - -#ifdef _WIN32 - -#endif -int sqlite3_shathree_init( - sqlite3 *db, - char **pzErrMsg, - const sqlite3_api_routines *pApi -){ - int rc = SQLITE_OK; - SQLITE_EXTENSION_INIT2(pApi); - (void)pzErrMsg; /* Unused parameter */ - rc = sqlite3_create_function(db, "sha3", 1, - SQLITE_UTF8 | SQLITE_INNOCUOUS | SQLITE_DETERMINISTIC, - 0, sha3Func, 0, 0); - if( rc==SQLITE_OK ){ - rc = sqlite3_create_function(db, "sha3", 2, - SQLITE_UTF8 | SQLITE_INNOCUOUS | SQLITE_DETERMINISTIC, - 0, sha3Func, 0, 0); - } - if( rc==SQLITE_OK ){ - rc = sqlite3_create_function(db, "sha3_query", 1, - SQLITE_UTF8 | SQLITE_DIRECTONLY, - 0, sha3QueryFunc, 0, 0); - } - if( rc==SQLITE_OK ){ - rc = sqlite3_create_function(db, "sha3_query", 2, - SQLITE_UTF8 | SQLITE_DIRECTONLY, - 0, sha3QueryFunc, 0, 0); - } - return rc; -} - -/************************* End ../ext/misc/shathree.c ********************/ -/************************* Begin ../ext/misc/fileio.c ******************/ -/* -** 2014-06-13 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -****************************************************************************** -** -** This SQLite extension implements SQL functions readfile() and -** writefile(), and eponymous virtual type "fsdir". -** -** WRITEFILE(FILE, DATA [, MODE [, MTIME]]): -** -** If neither of the optional arguments is present, then this UDF -** function writes blob DATA to file FILE. If successful, the number -** of bytes written is returned. If an error occurs, NULL is returned. -** -** If the first option argument - MODE - is present, then it must -** be passed an integer value that corresponds to a POSIX mode -** value (file type + permissions, as returned in the stat.st_mode -** field by the stat() system call). Three types of files may -** be written/created: -** -** regular files: (mode & 0170000)==0100000 -** symbolic links: (mode & 0170000)==0120000 -** directories: (mode & 0170000)==0040000 -** -** For a directory, the DATA is ignored. For a symbolic link, it is -** interpreted as text and used as the target of the link. For a -** regular file, it is interpreted as a blob and written into the -** named file. Regardless of the type of file, its permissions are -** set to (mode & 0777) before returning. -** -** If the optional MTIME argument is present, then it is interpreted -** as an integer - the number of seconds since the unix epoch. The -** modification-time of the target file is set to this value before -** returning. -** -** If three or more arguments are passed to this function and an -** error is encountered, an exception is raised. -** -** READFILE(FILE): -** -** Read and return the contents of file FILE (type blob) from disk. -** -** FSDIR: -** -** Used as follows: -** -** SELECT * FROM fsdir($path [, $dir]); -** -** Parameter $path is an absolute or relative pathname. If the file that it -** refers to does not exist, it is an error. If the path refers to a regular -** file or symbolic link, it returns a single row. Or, if the path refers -** to a directory, it returns one row for the directory, and one row for each -** file within the hierarchy rooted at $path. -** -** Each row has the following columns: -** -** name: Path to file or directory (text value). -** mode: Value of stat.st_mode for directory entry (an integer). -** mtime: Value of stat.st_mtime for directory entry (an integer). -** data: For a regular file, a blob containing the file data. For a -** symlink, a text value containing the text of the link. For a -** directory, NULL. -** -** If a non-NULL value is specified for the optional $dir parameter and -** $path is a relative path, then $path is interpreted relative to $dir. -** And the paths returned in the "name" column of the table are also -** relative to directory $dir. -*/ -/* #include "sqlite3ext.h" */ -SQLITE_EXTENSION_INIT1 -#include -#include -#include - -#include -#include -#include -#if !defined(_WIN32) && !defined(WIN32) -# include -# include -# include -# include -#else -# include "windows.h" -# include -# include -/* # include "test_windirent.h" */ -# define dirent DIRENT -# ifndef chmod -# define chmod _chmod -# endif -# ifndef stat -# define stat _stat -# endif -# define mkdir(path,mode) _mkdir(path) -# define lstat(path,buf) stat(path,buf) -#endif -#include -#include - - -/* -** Structure of the fsdir() table-valued function -*/ - /* 0 1 2 3 4 5 */ -#define FSDIR_SCHEMA "(name,mode,mtime,data,path HIDDEN,dir HIDDEN)" -#define FSDIR_COLUMN_NAME 0 /* Name of the file */ -#define FSDIR_COLUMN_MODE 1 /* Access mode */ -#define FSDIR_COLUMN_MTIME 2 /* Last modification time */ -#define FSDIR_COLUMN_DATA 3 /* File content */ -#define FSDIR_COLUMN_PATH 4 /* Path to top of search */ -#define FSDIR_COLUMN_DIR 5 /* Path is relative to this directory */ - - -/* -** Set the result stored by context ctx to a blob containing the -** contents of file zName. Or, leave the result unchanged (NULL) -** if the file does not exist or is unreadable. -** -** If the file exceeds the SQLite blob size limit, through an -** SQLITE_TOOBIG error. -** -** Throw an SQLITE_IOERR if there are difficulties pulling the file -** off of disk. -*/ -static void readFileContents(sqlite3_context *ctx, const char *zName){ - FILE *in; - sqlite3_int64 nIn; - void *pBuf; - sqlite3 *db; - int mxBlob; - - in = fopen(zName, "rb"); - if( in==0 ){ - /* File does not exist or is unreadable. Leave the result set to NULL. */ - return; - } - fseek(in, 0, SEEK_END); - nIn = ftell(in); - rewind(in); - db = sqlite3_context_db_handle(ctx); - mxBlob = sqlite3_limit(db, SQLITE_LIMIT_LENGTH, -1); - if( nIn>mxBlob ){ - sqlite3_result_error_code(ctx, SQLITE_TOOBIG); - fclose(in); - return; - } - pBuf = sqlite3_malloc64( nIn ? nIn : 1 ); - if( pBuf==0 ){ - sqlite3_result_error_nomem(ctx); - fclose(in); - return; - } - if( nIn==(sqlite3_int64)fread(pBuf, 1, (size_t)nIn, in) ){ - sqlite3_result_blob64(ctx, pBuf, nIn, sqlite3_free); - }else{ - sqlite3_result_error_code(ctx, SQLITE_IOERR); - sqlite3_free(pBuf); - } - fclose(in); -} - -/* -** Implementation of the "readfile(X)" SQL function. The entire content -** of the file named X is read and returned as a BLOB. NULL is returned -** if the file does not exist or is unreadable. -*/ -static void readfileFunc( - sqlite3_context *context, - int argc, - sqlite3_value **argv -){ - const char *zName; - (void)(argc); /* Unused parameter */ - zName = (const char*)sqlite3_value_text(argv[0]); - if( zName==0 ) return; - readFileContents(context, zName); -} - -/* -** Set the error message contained in context ctx to the results of -** vprintf(zFmt, ...). -*/ -static void ctxErrorMsg(sqlite3_context *ctx, const char *zFmt, ...){ - char *zMsg = 0; - va_list ap; - va_start(ap, zFmt); - zMsg = sqlite3_vmprintf(zFmt, ap); - sqlite3_result_error(ctx, zMsg, -1); - sqlite3_free(zMsg); - va_end(ap); -} - -#if defined(_WIN32) -/* -** This function is designed to convert a Win32 FILETIME structure into the -** number of seconds since the Unix Epoch (1970-01-01 00:00:00 UTC). -*/ -static sqlite3_uint64 fileTimeToUnixTime( - LPFILETIME pFileTime -){ - SYSTEMTIME epochSystemTime; - ULARGE_INTEGER epochIntervals; - FILETIME epochFileTime; - ULARGE_INTEGER fileIntervals; - - memset(&epochSystemTime, 0, sizeof(SYSTEMTIME)); - epochSystemTime.wYear = 1970; - epochSystemTime.wMonth = 1; - epochSystemTime.wDay = 1; - SystemTimeToFileTime(&epochSystemTime, &epochFileTime); - epochIntervals.LowPart = epochFileTime.dwLowDateTime; - epochIntervals.HighPart = epochFileTime.dwHighDateTime; - - fileIntervals.LowPart = pFileTime->dwLowDateTime; - fileIntervals.HighPart = pFileTime->dwHighDateTime; - - return (fileIntervals.QuadPart - epochIntervals.QuadPart) / 10000000; -} - -/* -** This function attempts to normalize the time values found in the stat() -** buffer to UTC. This is necessary on Win32, where the runtime library -** appears to return these values as local times. -*/ -static void statTimesToUtc( - const char *zPath, - struct stat *pStatBuf -){ - HANDLE hFindFile; - WIN32_FIND_DATAW fd; - LPWSTR zUnicodeName; - extern LPWSTR sqlite3_win32_utf8_to_unicode(const char*); - zUnicodeName = sqlite3_win32_utf8_to_unicode(zPath); - if( zUnicodeName ){ - memset(&fd, 0, sizeof(WIN32_FIND_DATAW)); - hFindFile = FindFirstFileW(zUnicodeName, &fd); - if( hFindFile!=NULL ){ - pStatBuf->st_ctime = (time_t)fileTimeToUnixTime(&fd.ftCreationTime); - pStatBuf->st_atime = (time_t)fileTimeToUnixTime(&fd.ftLastAccessTime); - pStatBuf->st_mtime = (time_t)fileTimeToUnixTime(&fd.ftLastWriteTime); - FindClose(hFindFile); - } - sqlite3_free(zUnicodeName); - } -} -#endif - -/* -** This function is used in place of stat(). On Windows, special handling -** is required in order for the included time to be returned as UTC. On all -** other systems, this function simply calls stat(). -*/ -static int fileStat( - const char *zPath, - struct stat *pStatBuf -){ -#if defined(_WIN32) - int rc = stat(zPath, pStatBuf); - if( rc==0 ) statTimesToUtc(zPath, pStatBuf); - return rc; -#else - return stat(zPath, pStatBuf); -#endif -} - -/* -** This function is used in place of lstat(). On Windows, special handling -** is required in order for the included time to be returned as UTC. On all -** other systems, this function simply calls lstat(). -*/ -static int fileLinkStat( - const char *zPath, - struct stat *pStatBuf -){ -#if defined(_WIN32) - int rc = lstat(zPath, pStatBuf); - if( rc==0 ) statTimesToUtc(zPath, pStatBuf); - return rc; -#else - return lstat(zPath, pStatBuf); -#endif -} - -/* -** Argument zFile is the name of a file that will be created and/or written -** by SQL function writefile(). This function ensures that the directory -** zFile will be written to exists, creating it if required. The permissions -** for any path components created by this function are set in accordance -** with the current umask. -** -** If an OOM condition is encountered, SQLITE_NOMEM is returned. Otherwise, -** SQLITE_OK is returned if the directory is successfully created, or -** SQLITE_ERROR otherwise. -*/ -static int makeDirectory( - const char *zFile -){ - char *zCopy = sqlite3_mprintf("%s", zFile); - int rc = SQLITE_OK; - - if( zCopy==0 ){ - rc = SQLITE_NOMEM; - }else{ - int nCopy = (int)strlen(zCopy); - int i = 1; - - while( rc==SQLITE_OK ){ - struct stat sStat; - int rc2; - - for(; zCopy[i]!='/' && i=0 ){ -#if defined(_WIN32) -#if !SQLITE_OS_WINRT - /* Windows */ - FILETIME lastAccess; - FILETIME lastWrite; - SYSTEMTIME currentTime; - LONGLONG intervals; - HANDLE hFile; - LPWSTR zUnicodeName; - extern LPWSTR sqlite3_win32_utf8_to_unicode(const char*); - - GetSystemTime(¤tTime); - SystemTimeToFileTime(¤tTime, &lastAccess); - intervals = Int32x32To64(mtime, 10000000) + 116444736000000000; - lastWrite.dwLowDateTime = (DWORD)intervals; - lastWrite.dwHighDateTime = intervals >> 32; - zUnicodeName = sqlite3_win32_utf8_to_unicode(zFile); - if( zUnicodeName==0 ){ - return 1; - } - hFile = CreateFileW( - zUnicodeName, FILE_WRITE_ATTRIBUTES, 0, NULL, OPEN_EXISTING, - FILE_FLAG_BACKUP_SEMANTICS, NULL - ); - sqlite3_free(zUnicodeName); - if( hFile!=INVALID_HANDLE_VALUE ){ - BOOL bResult = SetFileTime(hFile, NULL, &lastAccess, &lastWrite); - CloseHandle(hFile); - return !bResult; - }else{ - return 1; - } -#endif -#elif defined(AT_FDCWD) && 0 /* utimensat() is not universally available */ - /* Recent unix */ - struct timespec times[2]; - times[0].tv_nsec = times[1].tv_nsec = 0; - times[0].tv_sec = time(0); - times[1].tv_sec = mtime; - if( utimensat(AT_FDCWD, zFile, times, AT_SYMLINK_NOFOLLOW) ){ - return 1; - } -#else - /* Legacy unix */ - struct timeval times[2]; - times[0].tv_usec = times[1].tv_usec = 0; - times[0].tv_sec = time(0); - times[1].tv_sec = mtime; - if( utimes(zFile, times) ){ - return 1; - } -#endif - } - - return 0; -} - -/* -** Implementation of the "writefile(W,X[,Y[,Z]]])" SQL function. -** Refer to header comments at the top of this file for details. -*/ -static void writefileFunc( - sqlite3_context *context, - int argc, - sqlite3_value **argv -){ - const char *zFile; - mode_t mode = 0; - int res; - sqlite3_int64 mtime = -1; - - if( argc<2 || argc>4 ){ - sqlite3_result_error(context, - "wrong number of arguments to function writefile()", -1 - ); - return; - } - - zFile = (const char*)sqlite3_value_text(argv[0]); - if( zFile==0 ) return; - if( argc>=3 ){ - mode = (mode_t)sqlite3_value_int(argv[2]); - } - if( argc==4 ){ - mtime = sqlite3_value_int64(argv[3]); - } - - res = writeFile(context, zFile, argv[1], mode, mtime); - if( res==1 && errno==ENOENT ){ - if( makeDirectory(zFile)==SQLITE_OK ){ - res = writeFile(context, zFile, argv[1], mode, mtime); - } - } - - if( argc>2 && res!=0 ){ - if( S_ISLNK(mode) ){ - ctxErrorMsg(context, "failed to create symlink: %s", zFile); - }else if( S_ISDIR(mode) ){ - ctxErrorMsg(context, "failed to create directory: %s", zFile); - }else{ - ctxErrorMsg(context, "failed to write file: %s", zFile); - } - } -} - -/* -** SQL function: lsmode(MODE) -** -** Given a numberic st_mode from stat(), convert it into a human-readable -** text string in the style of "ls -l". -*/ -static void lsModeFunc( - sqlite3_context *context, - int argc, - sqlite3_value **argv -){ - int i; - int iMode = sqlite3_value_int(argv[0]); - char z[16]; - (void)argc; - if( S_ISLNK(iMode) ){ - z[0] = 'l'; - }else if( S_ISREG(iMode) ){ - z[0] = '-'; - }else if( S_ISDIR(iMode) ){ - z[0] = 'd'; - }else{ - z[0] = '?'; - } - for(i=0; i<3; i++){ - int m = (iMode >> ((2-i)*3)); - char *a = &z[1 + i*3]; - a[0] = (m & 0x4) ? 'r' : '-'; - a[1] = (m & 0x2) ? 'w' : '-'; - a[2] = (m & 0x1) ? 'x' : '-'; - } - z[10] = '\0'; - sqlite3_result_text(context, z, -1, SQLITE_TRANSIENT); -} - -#ifndef SQLITE_OMIT_VIRTUALTABLE - -/* -** Cursor type for recursively iterating through a directory structure. -*/ -typedef struct fsdir_cursor fsdir_cursor; -typedef struct FsdirLevel FsdirLevel; - -struct FsdirLevel { - DIR *pDir; /* From opendir() */ - char *zDir; /* Name of directory (nul-terminated) */ -}; - -struct fsdir_cursor { - sqlite3_vtab_cursor base; /* Base class - must be first */ - - int nLvl; /* Number of entries in aLvl[] array */ - int iLvl; /* Index of current entry */ - FsdirLevel *aLvl; /* Hierarchy of directories being traversed */ - - const char *zBase; - int nBase; - - struct stat sStat; /* Current lstat() results */ - char *zPath; /* Path to current entry */ - sqlite3_int64 iRowid; /* Current rowid */ -}; - -typedef struct fsdir_tab fsdir_tab; -struct fsdir_tab { - sqlite3_vtab base; /* Base class - must be first */ -}; - -/* -** Construct a new fsdir virtual table object. -*/ -static int fsdirConnect( - sqlite3 *db, - void *pAux, - int argc, const char *const*argv, - sqlite3_vtab **ppVtab, - char **pzErr -){ - fsdir_tab *pNew = 0; - int rc; - (void)pAux; - (void)argc; - (void)argv; - (void)pzErr; - rc = sqlite3_declare_vtab(db, "CREATE TABLE x" FSDIR_SCHEMA); - if( rc==SQLITE_OK ){ - pNew = (fsdir_tab*)sqlite3_malloc( sizeof(*pNew) ); - if( pNew==0 ) return SQLITE_NOMEM; - memset(pNew, 0, sizeof(*pNew)); - sqlite3_vtab_config(db, SQLITE_VTAB_DIRECTONLY); - } - *ppVtab = (sqlite3_vtab*)pNew; - return rc; -} - -/* -** This method is the destructor for fsdir vtab objects. -*/ -static int fsdirDisconnect(sqlite3_vtab *pVtab){ - sqlite3_free(pVtab); - return SQLITE_OK; -} - -/* -** Constructor for a new fsdir_cursor object. -*/ -static int fsdirOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){ - fsdir_cursor *pCur; - (void)p; - pCur = sqlite3_malloc( sizeof(*pCur) ); - if( pCur==0 ) return SQLITE_NOMEM; - memset(pCur, 0, sizeof(*pCur)); - pCur->iLvl = -1; - *ppCursor = &pCur->base; - return SQLITE_OK; -} - -/* -** Reset a cursor back to the state it was in when first returned -** by fsdirOpen(). -*/ -static void fsdirResetCursor(fsdir_cursor *pCur){ - int i; - for(i=0; i<=pCur->iLvl; i++){ - FsdirLevel *pLvl = &pCur->aLvl[i]; - if( pLvl->pDir ) closedir(pLvl->pDir); - sqlite3_free(pLvl->zDir); - } - sqlite3_free(pCur->zPath); - sqlite3_free(pCur->aLvl); - pCur->aLvl = 0; - pCur->zPath = 0; - pCur->zBase = 0; - pCur->nBase = 0; - pCur->nLvl = 0; - pCur->iLvl = -1; - pCur->iRowid = 1; -} - -/* -** Destructor for an fsdir_cursor. -*/ -static int fsdirClose(sqlite3_vtab_cursor *cur){ - fsdir_cursor *pCur = (fsdir_cursor*)cur; - - fsdirResetCursor(pCur); - sqlite3_free(pCur); - return SQLITE_OK; -} - -/* -** Set the error message for the virtual table associated with cursor -** pCur to the results of vprintf(zFmt, ...). -*/ -static void fsdirSetErrmsg(fsdir_cursor *pCur, const char *zFmt, ...){ - va_list ap; - va_start(ap, zFmt); - pCur->base.pVtab->zErrMsg = sqlite3_vmprintf(zFmt, ap); - va_end(ap); -} - - -/* -** Advance an fsdir_cursor to its next row of output. -*/ -static int fsdirNext(sqlite3_vtab_cursor *cur){ - fsdir_cursor *pCur = (fsdir_cursor*)cur; - mode_t m = pCur->sStat.st_mode; - - pCur->iRowid++; - if( S_ISDIR(m) ){ - /* Descend into this directory */ - int iNew = pCur->iLvl + 1; - FsdirLevel *pLvl; - if( iNew>=pCur->nLvl ){ - int nNew = iNew+1; - sqlite3_int64 nByte = nNew*sizeof(FsdirLevel); - FsdirLevel *aNew = (FsdirLevel*)sqlite3_realloc64(pCur->aLvl, nByte); - if( aNew==0 ) return SQLITE_NOMEM; - memset(&aNew[pCur->nLvl], 0, sizeof(FsdirLevel)*(nNew-pCur->nLvl)); - pCur->aLvl = aNew; - pCur->nLvl = nNew; - } - pCur->iLvl = iNew; - pLvl = &pCur->aLvl[iNew]; - - pLvl->zDir = pCur->zPath; - pCur->zPath = 0; - pLvl->pDir = opendir(pLvl->zDir); - if( pLvl->pDir==0 ){ - fsdirSetErrmsg(pCur, "cannot read directory: %s", pCur->zPath); - return SQLITE_ERROR; - } - } - - while( pCur->iLvl>=0 ){ - FsdirLevel *pLvl = &pCur->aLvl[pCur->iLvl]; - struct dirent *pEntry = readdir(pLvl->pDir); - if( pEntry ){ - if( pEntry->d_name[0]=='.' ){ - if( pEntry->d_name[1]=='.' && pEntry->d_name[2]=='\0' ) continue; - if( pEntry->d_name[1]=='\0' ) continue; - } - sqlite3_free(pCur->zPath); - pCur->zPath = sqlite3_mprintf("%s/%s", pLvl->zDir, pEntry->d_name); - if( pCur->zPath==0 ) return SQLITE_NOMEM; - if( fileLinkStat(pCur->zPath, &pCur->sStat) ){ - fsdirSetErrmsg(pCur, "cannot stat file: %s", pCur->zPath); - return SQLITE_ERROR; - } - return SQLITE_OK; - } - closedir(pLvl->pDir); - sqlite3_free(pLvl->zDir); - pLvl->pDir = 0; - pLvl->zDir = 0; - pCur->iLvl--; - } - - /* EOF */ - sqlite3_free(pCur->zPath); - pCur->zPath = 0; - return SQLITE_OK; -} - -/* -** Return values of columns for the row at which the series_cursor -** is currently pointing. -*/ -static int fsdirColumn( - sqlite3_vtab_cursor *cur, /* The cursor */ - sqlite3_context *ctx, /* First argument to sqlite3_result_...() */ - int i /* Which column to return */ -){ - fsdir_cursor *pCur = (fsdir_cursor*)cur; - switch( i ){ - case FSDIR_COLUMN_NAME: { - sqlite3_result_text(ctx, &pCur->zPath[pCur->nBase], -1, SQLITE_TRANSIENT); - break; - } - - case FSDIR_COLUMN_MODE: - sqlite3_result_int64(ctx, pCur->sStat.st_mode); - break; - - case FSDIR_COLUMN_MTIME: - sqlite3_result_int64(ctx, pCur->sStat.st_mtime); - break; - - case FSDIR_COLUMN_DATA: { - mode_t m = pCur->sStat.st_mode; - if( S_ISDIR(m) ){ - sqlite3_result_null(ctx); -#if !defined(_WIN32) && !defined(WIN32) - }else if( S_ISLNK(m) ){ - char aStatic[64]; - char *aBuf = aStatic; - sqlite3_int64 nBuf = 64; - int n; - - while( 1 ){ - n = readlink(pCur->zPath, aBuf, nBuf); - if( nzPath); - } - } - case FSDIR_COLUMN_PATH: - default: { - /* The FSDIR_COLUMN_PATH and FSDIR_COLUMN_DIR are input parameters. - ** always return their values as NULL */ - break; - } - } - return SQLITE_OK; -} - -/* -** Return the rowid for the current row. In this implementation, the -** first row returned is assigned rowid value 1, and each subsequent -** row a value 1 more than that of the previous. -*/ -static int fsdirRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){ - fsdir_cursor *pCur = (fsdir_cursor*)cur; - *pRowid = pCur->iRowid; - return SQLITE_OK; -} - -/* -** Return TRUE if the cursor has been moved off of the last -** row of output. -*/ -static int fsdirEof(sqlite3_vtab_cursor *cur){ - fsdir_cursor *pCur = (fsdir_cursor*)cur; - return (pCur->zPath==0); -} - -/* -** xFilter callback. -** -** idxNum==1 PATH parameter only -** idxNum==2 Both PATH and DIR supplied -*/ -static int fsdirFilter( - sqlite3_vtab_cursor *cur, - int idxNum, const char *idxStr, - int argc, sqlite3_value **argv -){ - const char *zDir = 0; - fsdir_cursor *pCur = (fsdir_cursor*)cur; - (void)idxStr; - fsdirResetCursor(pCur); - - if( idxNum==0 ){ - fsdirSetErrmsg(pCur, "table function fsdir requires an argument"); - return SQLITE_ERROR; - } - - assert( argc==idxNum && (argc==1 || argc==2) ); - zDir = (const char*)sqlite3_value_text(argv[0]); - if( zDir==0 ){ - fsdirSetErrmsg(pCur, "table function fsdir requires a non-NULL argument"); - return SQLITE_ERROR; - } - if( argc==2 ){ - pCur->zBase = (const char*)sqlite3_value_text(argv[1]); - } - if( pCur->zBase ){ - pCur->nBase = (int)strlen(pCur->zBase)+1; - pCur->zPath = sqlite3_mprintf("%s/%s", pCur->zBase, zDir); - }else{ - pCur->zPath = sqlite3_mprintf("%s", zDir); - } - - if( pCur->zPath==0 ){ - return SQLITE_NOMEM; - } - if( fileLinkStat(pCur->zPath, &pCur->sStat) ){ - fsdirSetErrmsg(pCur, "cannot stat file: %s", pCur->zPath); - return SQLITE_ERROR; - } - - return SQLITE_OK; -} - -/* -** SQLite will invoke this method one or more times while planning a query -** that uses the generate_series virtual table. This routine needs to create -** a query plan for each invocation and compute an estimated cost for that -** plan. -** -** In this implementation idxNum is used to represent the -** query plan. idxStr is unused. -** -** The query plan is represented by values of idxNum: -** -** (1) The path value is supplied by argv[0] -** (2) Path is in argv[0] and dir is in argv[1] -*/ -static int fsdirBestIndex( - sqlite3_vtab *tab, - sqlite3_index_info *pIdxInfo -){ - int i; /* Loop over constraints */ - int idxPath = -1; /* Index in pIdxInfo->aConstraint of PATH= */ - int idxDir = -1; /* Index in pIdxInfo->aConstraint of DIR= */ - int seenPath = 0; /* True if an unusable PATH= constraint is seen */ - int seenDir = 0; /* True if an unusable DIR= constraint is seen */ - const struct sqlite3_index_constraint *pConstraint; - - (void)tab; - pConstraint = pIdxInfo->aConstraint; - for(i=0; inConstraint; i++, pConstraint++){ - if( pConstraint->op!=SQLITE_INDEX_CONSTRAINT_EQ ) continue; - switch( pConstraint->iColumn ){ - case FSDIR_COLUMN_PATH: { - if( pConstraint->usable ){ - idxPath = i; - seenPath = 0; - }else if( idxPath<0 ){ - seenPath = 1; - } - break; - } - case FSDIR_COLUMN_DIR: { - if( pConstraint->usable ){ - idxDir = i; - seenDir = 0; - }else if( idxDir<0 ){ - seenDir = 1; - } - break; - } - } - } - if( seenPath || seenDir ){ - /* If input parameters are unusable, disallow this plan */ - return SQLITE_CONSTRAINT; - } - - if( idxPath<0 ){ - pIdxInfo->idxNum = 0; - /* The pIdxInfo->estimatedCost should have been initialized to a huge - ** number. Leave it unchanged. */ - pIdxInfo->estimatedRows = 0x7fffffff; - }else{ - pIdxInfo->aConstraintUsage[idxPath].omit = 1; - pIdxInfo->aConstraintUsage[idxPath].argvIndex = 1; - if( idxDir>=0 ){ - pIdxInfo->aConstraintUsage[idxDir].omit = 1; - pIdxInfo->aConstraintUsage[idxDir].argvIndex = 2; - pIdxInfo->idxNum = 2; - pIdxInfo->estimatedCost = 10.0; - }else{ - pIdxInfo->idxNum = 1; - pIdxInfo->estimatedCost = 100.0; - } - } - - return SQLITE_OK; -} - -/* -** Register the "fsdir" virtual table. -*/ -static int fsdirRegister(sqlite3 *db){ - static sqlite3_module fsdirModule = { - 0, /* iVersion */ - 0, /* xCreate */ - fsdirConnect, /* xConnect */ - fsdirBestIndex, /* xBestIndex */ - fsdirDisconnect, /* xDisconnect */ - 0, /* xDestroy */ - fsdirOpen, /* xOpen - open a cursor */ - fsdirClose, /* xClose - close a cursor */ - fsdirFilter, /* xFilter - configure scan constraints */ - fsdirNext, /* xNext - advance a cursor */ - fsdirEof, /* xEof - check for end of scan */ - fsdirColumn, /* xColumn - read data */ - fsdirRowid, /* xRowid - read data */ - 0, /* xUpdate */ - 0, /* xBegin */ - 0, /* xSync */ - 0, /* xCommit */ - 0, /* xRollback */ - 0, /* xFindMethod */ - 0, /* xRename */ - 0, /* xSavepoint */ - 0, /* xRelease */ - 0, /* xRollbackTo */ - 0, /* xShadowName */ - }; - - int rc = sqlite3_create_module(db, "fsdir", &fsdirModule, 0); - return rc; -} -#else /* SQLITE_OMIT_VIRTUALTABLE */ -# define fsdirRegister(x) SQLITE_OK -#endif - -#ifdef _WIN32 - -#endif -int sqlite3_fileio_init( - sqlite3 *db, - char **pzErrMsg, - const sqlite3_api_routines *pApi -){ - int rc = SQLITE_OK; - SQLITE_EXTENSION_INIT2(pApi); - (void)pzErrMsg; /* Unused parameter */ - rc = sqlite3_create_function(db, "readfile", 1, - SQLITE_UTF8|SQLITE_DIRECTONLY, 0, - readfileFunc, 0, 0); - if( rc==SQLITE_OK ){ - rc = sqlite3_create_function(db, "writefile", -1, - SQLITE_UTF8|SQLITE_DIRECTONLY, 0, - writefileFunc, 0, 0); - } - if( rc==SQLITE_OK ){ - rc = sqlite3_create_function(db, "lsmode", 1, SQLITE_UTF8, 0, - lsModeFunc, 0, 0); - } - if( rc==SQLITE_OK ){ - rc = fsdirRegister(db); - } - return rc; -} - -/************************* End ../ext/misc/fileio.c ********************/ -/************************* Begin ../ext/misc/completion.c ******************/ -/* -** 2017-07-10 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** -** This file implements an eponymous virtual table that returns suggested -** completions for a partial SQL input. -** -** Suggested usage: -** -** SELECT DISTINCT candidate COLLATE nocase -** FROM completion($prefix,$wholeline) -** ORDER BY 1; -** -** The two query parameters are optional. $prefix is the text of the -** current word being typed and that is to be completed. $wholeline is -** the complete input line, used for context. -** -** The raw completion() table might return the same candidate multiple -** times, for example if the same column name is used to two or more -** tables. And the candidates are returned in an arbitrary order. Hence, -** the DISTINCT and ORDER BY are recommended. -** -** This virtual table operates at the speed of human typing, and so there -** is no attempt to make it fast. Even a slow implementation will be much -** faster than any human can type. -** -*/ -/* #include "sqlite3ext.h" */ -SQLITE_EXTENSION_INIT1 -#include -#include -#include - -#ifndef SQLITE_OMIT_VIRTUALTABLE - -/* completion_vtab is a subclass of sqlite3_vtab which will -** serve as the underlying representation of a completion virtual table -*/ -typedef struct completion_vtab completion_vtab; -struct completion_vtab { - sqlite3_vtab base; /* Base class - must be first */ - sqlite3 *db; /* Database connection for this completion vtab */ -}; - -/* completion_cursor is a subclass of sqlite3_vtab_cursor which will -** serve as the underlying representation of a cursor that scans -** over rows of the result -*/ -typedef struct completion_cursor completion_cursor; -struct completion_cursor { - sqlite3_vtab_cursor base; /* Base class - must be first */ - sqlite3 *db; /* Database connection for this cursor */ - int nPrefix, nLine; /* Number of bytes in zPrefix and zLine */ - char *zPrefix; /* The prefix for the word we want to complete */ - char *zLine; /* The whole that we want to complete */ - const char *zCurrentRow; /* Current output row */ - int szRow; /* Length of the zCurrentRow string */ - sqlite3_stmt *pStmt; /* Current statement */ - sqlite3_int64 iRowid; /* The rowid */ - int ePhase; /* Current phase */ - int j; /* inter-phase counter */ -}; - -/* Values for ePhase: -*/ -#define COMPLETION_FIRST_PHASE 1 -#define COMPLETION_KEYWORDS 1 -#define COMPLETION_PRAGMAS 2 -#define COMPLETION_FUNCTIONS 3 -#define COMPLETION_COLLATIONS 4 -#define COMPLETION_INDEXES 5 -#define COMPLETION_TRIGGERS 6 -#define COMPLETION_DATABASES 7 -#define COMPLETION_TABLES 8 /* Also VIEWs and TRIGGERs */ -#define COMPLETION_COLUMNS 9 -#define COMPLETION_MODULES 10 -#define COMPLETION_EOF 11 - -/* -** The completionConnect() method is invoked to create a new -** completion_vtab that describes the completion virtual table. -** -** Think of this routine as the constructor for completion_vtab objects. -** -** All this routine needs to do is: -** -** (1) Allocate the completion_vtab object and initialize all fields. -** -** (2) Tell SQLite (via the sqlite3_declare_vtab() interface) what the -** result set of queries against completion will look like. -*/ -static int completionConnect( - sqlite3 *db, - void *pAux, - int argc, const char *const*argv, - sqlite3_vtab **ppVtab, - char **pzErr -){ - completion_vtab *pNew; - int rc; - - (void)(pAux); /* Unused parameter */ - (void)(argc); /* Unused parameter */ - (void)(argv); /* Unused parameter */ - (void)(pzErr); /* Unused parameter */ - -/* Column numbers */ -#define COMPLETION_COLUMN_CANDIDATE 0 /* Suggested completion of the input */ -#define COMPLETION_COLUMN_PREFIX 1 /* Prefix of the word to be completed */ -#define COMPLETION_COLUMN_WHOLELINE 2 /* Entire line seen so far */ -#define COMPLETION_COLUMN_PHASE 3 /* ePhase - used for debugging only */ - - sqlite3_vtab_config(db, SQLITE_VTAB_INNOCUOUS); - rc = sqlite3_declare_vtab(db, - "CREATE TABLE x(" - " candidate TEXT," - " prefix TEXT HIDDEN," - " wholeline TEXT HIDDEN," - " phase INT HIDDEN" /* Used for debugging only */ - ")"); - if( rc==SQLITE_OK ){ - pNew = sqlite3_malloc( sizeof(*pNew) ); - *ppVtab = (sqlite3_vtab*)pNew; - if( pNew==0 ) return SQLITE_NOMEM; - memset(pNew, 0, sizeof(*pNew)); - pNew->db = db; - } - return rc; -} - -/* -** This method is the destructor for completion_cursor objects. -*/ -static int completionDisconnect(sqlite3_vtab *pVtab){ - sqlite3_free(pVtab); - return SQLITE_OK; -} - -/* -** Constructor for a new completion_cursor object. -*/ -static int completionOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){ - completion_cursor *pCur; - pCur = sqlite3_malloc( sizeof(*pCur) ); - if( pCur==0 ) return SQLITE_NOMEM; - memset(pCur, 0, sizeof(*pCur)); - pCur->db = ((completion_vtab*)p)->db; - *ppCursor = &pCur->base; - return SQLITE_OK; -} - -/* -** Reset the completion_cursor. -*/ -static void completionCursorReset(completion_cursor *pCur){ - sqlite3_free(pCur->zPrefix); pCur->zPrefix = 0; pCur->nPrefix = 0; - sqlite3_free(pCur->zLine); pCur->zLine = 0; pCur->nLine = 0; - sqlite3_finalize(pCur->pStmt); pCur->pStmt = 0; - pCur->j = 0; -} - -/* -** Destructor for a completion_cursor. -*/ -static int completionClose(sqlite3_vtab_cursor *cur){ - completionCursorReset((completion_cursor*)cur); - sqlite3_free(cur); - return SQLITE_OK; -} - -/* -** Advance a completion_cursor to its next row of output. -** -** The ->ePhase, ->j, and ->pStmt fields of the completion_cursor object -** record the current state of the scan. This routine sets ->zCurrentRow -** to the current row of output and then returns. If no more rows remain, -** then ->ePhase is set to COMPLETION_EOF which will signal the virtual -** table that has reached the end of its scan. -** -** The current implementation just lists potential identifiers and -** keywords and filters them by zPrefix. Future enhancements should -** take zLine into account to try to restrict the set of identifiers and -** keywords based on what would be legal at the current point of input. -*/ -static int completionNext(sqlite3_vtab_cursor *cur){ - completion_cursor *pCur = (completion_cursor*)cur; - int eNextPhase = 0; /* Next phase to try if current phase reaches end */ - int iCol = -1; /* If >=0, step pCur->pStmt and use the i-th column */ - pCur->iRowid++; - while( pCur->ePhase!=COMPLETION_EOF ){ - switch( pCur->ePhase ){ - case COMPLETION_KEYWORDS: { - if( pCur->j >= sqlite3_keyword_count() ){ - pCur->zCurrentRow = 0; - pCur->ePhase = COMPLETION_DATABASES; - }else{ - sqlite3_keyword_name(pCur->j++, &pCur->zCurrentRow, &pCur->szRow); - } - iCol = -1; - break; - } - case COMPLETION_DATABASES: { - if( pCur->pStmt==0 ){ - sqlite3_prepare_v2(pCur->db, "PRAGMA database_list", -1, - &pCur->pStmt, 0); - } - iCol = 1; - eNextPhase = COMPLETION_TABLES; - break; - } - case COMPLETION_TABLES: { - if( pCur->pStmt==0 ){ - sqlite3_stmt *pS2; - char *zSql = 0; - const char *zSep = ""; - sqlite3_prepare_v2(pCur->db, "PRAGMA database_list", -1, &pS2, 0); - while( sqlite3_step(pS2)==SQLITE_ROW ){ - const char *zDb = (const char*)sqlite3_column_text(pS2, 1); - zSql = sqlite3_mprintf( - "%z%s" - "SELECT name FROM \"%w\".sqlite_schema", - zSql, zSep, zDb - ); - if( zSql==0 ) return SQLITE_NOMEM; - zSep = " UNION "; - } - sqlite3_finalize(pS2); - sqlite3_prepare_v2(pCur->db, zSql, -1, &pCur->pStmt, 0); - sqlite3_free(zSql); - } - iCol = 0; - eNextPhase = COMPLETION_COLUMNS; - break; - } - case COMPLETION_COLUMNS: { - if( pCur->pStmt==0 ){ - sqlite3_stmt *pS2; - char *zSql = 0; - const char *zSep = ""; - sqlite3_prepare_v2(pCur->db, "PRAGMA database_list", -1, &pS2, 0); - while( sqlite3_step(pS2)==SQLITE_ROW ){ - const char *zDb = (const char*)sqlite3_column_text(pS2, 1); - zSql = sqlite3_mprintf( - "%z%s" - "SELECT pti.name FROM \"%w\".sqlite_schema AS sm" - " JOIN pragma_table_info(sm.name,%Q) AS pti" - " WHERE sm.type='table'", - zSql, zSep, zDb, zDb - ); - if( zSql==0 ) return SQLITE_NOMEM; - zSep = " UNION "; - } - sqlite3_finalize(pS2); - sqlite3_prepare_v2(pCur->db, zSql, -1, &pCur->pStmt, 0); - sqlite3_free(zSql); - } - iCol = 0; - eNextPhase = COMPLETION_EOF; - break; - } - } - if( iCol<0 ){ - /* This case is when the phase presets zCurrentRow */ - if( pCur->zCurrentRow==0 ) continue; - }else{ - if( sqlite3_step(pCur->pStmt)==SQLITE_ROW ){ - /* Extract the next row of content */ - pCur->zCurrentRow = (const char*)sqlite3_column_text(pCur->pStmt, iCol); - pCur->szRow = sqlite3_column_bytes(pCur->pStmt, iCol); - }else{ - /* When all rows are finished, advance to the next phase */ - sqlite3_finalize(pCur->pStmt); - pCur->pStmt = 0; - pCur->ePhase = eNextPhase; - continue; - } - } - if( pCur->nPrefix==0 ) break; - if( pCur->nPrefix<=pCur->szRow - && sqlite3_strnicmp(pCur->zPrefix, pCur->zCurrentRow, pCur->nPrefix)==0 - ){ - break; - } - } - - return SQLITE_OK; -} - -/* -** Return values of columns for the row at which the completion_cursor -** is currently pointing. -*/ -static int completionColumn( - sqlite3_vtab_cursor *cur, /* The cursor */ - sqlite3_context *ctx, /* First argument to sqlite3_result_...() */ - int i /* Which column to return */ -){ - completion_cursor *pCur = (completion_cursor*)cur; - switch( i ){ - case COMPLETION_COLUMN_CANDIDATE: { - sqlite3_result_text(ctx, pCur->zCurrentRow, pCur->szRow,SQLITE_TRANSIENT); - break; - } - case COMPLETION_COLUMN_PREFIX: { - sqlite3_result_text(ctx, pCur->zPrefix, -1, SQLITE_TRANSIENT); - break; - } - case COMPLETION_COLUMN_WHOLELINE: { - sqlite3_result_text(ctx, pCur->zLine, -1, SQLITE_TRANSIENT); - break; - } - case COMPLETION_COLUMN_PHASE: { - sqlite3_result_int(ctx, pCur->ePhase); - break; - } - } - return SQLITE_OK; -} - -/* -** Return the rowid for the current row. In this implementation, the -** rowid is the same as the output value. -*/ -static int completionRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){ - completion_cursor *pCur = (completion_cursor*)cur; - *pRowid = pCur->iRowid; - return SQLITE_OK; -} - -/* -** Return TRUE if the cursor has been moved off of the last -** row of output. -*/ -static int completionEof(sqlite3_vtab_cursor *cur){ - completion_cursor *pCur = (completion_cursor*)cur; - return pCur->ePhase >= COMPLETION_EOF; -} - -/* -** This method is called to "rewind" the completion_cursor object back -** to the first row of output. This method is always called at least -** once prior to any call to completionColumn() or completionRowid() or -** completionEof(). -*/ -static int completionFilter( - sqlite3_vtab_cursor *pVtabCursor, - int idxNum, const char *idxStr, - int argc, sqlite3_value **argv -){ - completion_cursor *pCur = (completion_cursor *)pVtabCursor; - int iArg = 0; - (void)(idxStr); /* Unused parameter */ - (void)(argc); /* Unused parameter */ - completionCursorReset(pCur); - if( idxNum & 1 ){ - pCur->nPrefix = sqlite3_value_bytes(argv[iArg]); - if( pCur->nPrefix>0 ){ - pCur->zPrefix = sqlite3_mprintf("%s", sqlite3_value_text(argv[iArg])); - if( pCur->zPrefix==0 ) return SQLITE_NOMEM; - } - iArg = 1; - } - if( idxNum & 2 ){ - pCur->nLine = sqlite3_value_bytes(argv[iArg]); - if( pCur->nLine>0 ){ - pCur->zLine = sqlite3_mprintf("%s", sqlite3_value_text(argv[iArg])); - if( pCur->zLine==0 ) return SQLITE_NOMEM; - } - } - if( pCur->zLine!=0 && pCur->zPrefix==0 ){ - int i = pCur->nLine; - while( i>0 && (isalnum(pCur->zLine[i-1]) || pCur->zLine[i-1]=='_') ){ - i--; - } - pCur->nPrefix = pCur->nLine - i; - if( pCur->nPrefix>0 ){ - pCur->zPrefix = sqlite3_mprintf("%.*s", pCur->nPrefix, pCur->zLine + i); - if( pCur->zPrefix==0 ) return SQLITE_NOMEM; - } - } - pCur->iRowid = 0; - pCur->ePhase = COMPLETION_FIRST_PHASE; - return completionNext(pVtabCursor); -} - -/* -** SQLite will invoke this method one or more times while planning a query -** that uses the completion virtual table. This routine needs to create -** a query plan for each invocation and compute an estimated cost for that -** plan. -** -** There are two hidden parameters that act as arguments to the table-valued -** function: "prefix" and "wholeline". Bit 0 of idxNum is set if "prefix" -** is available and bit 1 is set if "wholeline" is available. -*/ -static int completionBestIndex( - sqlite3_vtab *tab, - sqlite3_index_info *pIdxInfo -){ - int i; /* Loop over constraints */ - int idxNum = 0; /* The query plan bitmask */ - int prefixIdx = -1; /* Index of the start= constraint, or -1 if none */ - int wholelineIdx = -1; /* Index of the stop= constraint, or -1 if none */ - int nArg = 0; /* Number of arguments that completeFilter() expects */ - const struct sqlite3_index_constraint *pConstraint; - - (void)(tab); /* Unused parameter */ - pConstraint = pIdxInfo->aConstraint; - for(i=0; inConstraint; i++, pConstraint++){ - if( pConstraint->usable==0 ) continue; - if( pConstraint->op!=SQLITE_INDEX_CONSTRAINT_EQ ) continue; - switch( pConstraint->iColumn ){ - case COMPLETION_COLUMN_PREFIX: - prefixIdx = i; - idxNum |= 1; - break; - case COMPLETION_COLUMN_WHOLELINE: - wholelineIdx = i; - idxNum |= 2; - break; - } - } - if( prefixIdx>=0 ){ - pIdxInfo->aConstraintUsage[prefixIdx].argvIndex = ++nArg; - pIdxInfo->aConstraintUsage[prefixIdx].omit = 1; - } - if( wholelineIdx>=0 ){ - pIdxInfo->aConstraintUsage[wholelineIdx].argvIndex = ++nArg; - pIdxInfo->aConstraintUsage[wholelineIdx].omit = 1; - } - pIdxInfo->idxNum = idxNum; - pIdxInfo->estimatedCost = (double)5000 - 1000*nArg; - pIdxInfo->estimatedRows = 500 - 100*nArg; - return SQLITE_OK; -} - -/* -** This following structure defines all the methods for the -** completion virtual table. -*/ -static sqlite3_module completionModule = { - 0, /* iVersion */ - 0, /* xCreate */ - completionConnect, /* xConnect */ - completionBestIndex, /* xBestIndex */ - completionDisconnect, /* xDisconnect */ - 0, /* xDestroy */ - completionOpen, /* xOpen - open a cursor */ - completionClose, /* xClose - close a cursor */ - completionFilter, /* xFilter - configure scan constraints */ - completionNext, /* xNext - advance a cursor */ - completionEof, /* xEof - check for end of scan */ - completionColumn, /* xColumn - read data */ - completionRowid, /* xRowid - read data */ - 0, /* xUpdate */ - 0, /* xBegin */ - 0, /* xSync */ - 0, /* xCommit */ - 0, /* xRollback */ - 0, /* xFindMethod */ - 0, /* xRename */ - 0, /* xSavepoint */ - 0, /* xRelease */ - 0, /* xRollbackTo */ - 0 /* xShadowName */ -}; - -#endif /* SQLITE_OMIT_VIRTUALTABLE */ - -int sqlite3CompletionVtabInit(sqlite3 *db){ - int rc = SQLITE_OK; -#ifndef SQLITE_OMIT_VIRTUALTABLE - rc = sqlite3_create_module(db, "completion", &completionModule, 0); -#endif - return rc; -} - -#ifdef _WIN32 - -#endif -int sqlite3_completion_init( - sqlite3 *db, - char **pzErrMsg, - const sqlite3_api_routines *pApi -){ - int rc = SQLITE_OK; - SQLITE_EXTENSION_INIT2(pApi); - (void)(pzErrMsg); /* Unused parameter */ -#ifndef SQLITE_OMIT_VIRTUALTABLE - rc = sqlite3CompletionVtabInit(db); -#endif - return rc; -} - -/************************* End ../ext/misc/completion.c ********************/ -/************************* Begin ../ext/misc/appendvfs.c ******************/ -/* -** 2017-10-20 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -****************************************************************************** -** -** This file implements a VFS shim that allows an SQLite database to be -** appended onto the end of some other file, such as an executable. -** -** A special record must appear at the end of the file that identifies the -** file as an appended database and provides an offset to page 1. For -** best performance page 1 should be located at a disk page boundary, though -** that is not required. -** -** When opening a database using this VFS, the connection might treat -** the file as an ordinary SQLite database, or it might treat is as a -** database appended onto some other file. Here are the rules: -** -** (1) When opening a new empty file, that file is treated as an ordinary -** database. -** -** (2) When opening a file that begins with the standard SQLite prefix -** string "SQLite format 3", that file is treated as an ordinary -** database. -** -** (3) When opening a file that ends with the appendvfs trailer string -** "Start-Of-SQLite3-NNNNNNNN" that file is treated as an appended -** database. -** -** (4) If none of the above apply and the SQLITE_OPEN_CREATE flag is -** set, then a new database is appended to the already existing file. -** -** (5) Otherwise, SQLITE_CANTOPEN is returned. -** -** To avoid unnecessary complications with the PENDING_BYTE, the size of -** the file containing the database is limited to 1GB. This VFS will refuse -** to read or write past the 1GB mark. This restriction might be lifted in -** future versions. For now, if you need a large database, then keep the -** database in a separate file. -** -** If the file being opened is not an appended database, then this shim is -** a pass-through into the default underlying VFS. -**/ -/* #include "sqlite3ext.h" */ -SQLITE_EXTENSION_INIT1 -#include -#include - -/* The append mark at the end of the database is: -** -** Start-Of-SQLite3-NNNNNNNN -** 123456789 123456789 12345 -** -** The NNNNNNNN represents a 64-bit big-endian unsigned integer which is -** the offset to page 1. -*/ -#define APND_MARK_PREFIX "Start-Of-SQLite3-" -#define APND_MARK_PREFIX_SZ 17 -#define APND_MARK_SIZE 25 - -/* -** Maximum size of the combined prefix + database + append-mark. This -** must be less than 0x40000000 to avoid locking issues on Windows. -*/ -#define APND_MAX_SIZE (65536*15259) - -/* -** Forward declaration of objects used by this utility -*/ -typedef struct sqlite3_vfs ApndVfs; -typedef struct ApndFile ApndFile; - -/* Access to a lower-level VFS that (might) implement dynamic loading, -** access to randomness, etc. -*/ -#define ORIGVFS(p) ((sqlite3_vfs*)((p)->pAppData)) -#define ORIGFILE(p) ((sqlite3_file*)(((ApndFile*)(p))+1)) - -/* An open file */ -struct ApndFile { - sqlite3_file base; /* IO methods */ - sqlite3_int64 iPgOne; /* File offset to page 1 */ - sqlite3_int64 iMark; /* Start of the append-mark */ -}; - -/* -** Methods for ApndFile -*/ -static int apndClose(sqlite3_file*); -static int apndRead(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst); -static int apndWrite(sqlite3_file*,const void*,int iAmt, sqlite3_int64 iOfst); -static int apndTruncate(sqlite3_file*, sqlite3_int64 size); -static int apndSync(sqlite3_file*, int flags); -static int apndFileSize(sqlite3_file*, sqlite3_int64 *pSize); -static int apndLock(sqlite3_file*, int); -static int apndUnlock(sqlite3_file*, int); -static int apndCheckReservedLock(sqlite3_file*, int *pResOut); -static int apndFileControl(sqlite3_file*, int op, void *pArg); -static int apndSectorSize(sqlite3_file*); -static int apndDeviceCharacteristics(sqlite3_file*); -static int apndShmMap(sqlite3_file*, int iPg, int pgsz, int, void volatile**); -static int apndShmLock(sqlite3_file*, int offset, int n, int flags); -static void apndShmBarrier(sqlite3_file*); -static int apndShmUnmap(sqlite3_file*, int deleteFlag); -static int apndFetch(sqlite3_file*, sqlite3_int64 iOfst, int iAmt, void **pp); -static int apndUnfetch(sqlite3_file*, sqlite3_int64 iOfst, void *p); - -/* -** Methods for ApndVfs -*/ -static int apndOpen(sqlite3_vfs*, const char *, sqlite3_file*, int , int *); -static int apndDelete(sqlite3_vfs*, const char *zName, int syncDir); -static int apndAccess(sqlite3_vfs*, const char *zName, int flags, int *); -static int apndFullPathname(sqlite3_vfs*, const char *zName, int, char *zOut); -static void *apndDlOpen(sqlite3_vfs*, const char *zFilename); -static void apndDlError(sqlite3_vfs*, int nByte, char *zErrMsg); -static void (*apndDlSym(sqlite3_vfs *pVfs, void *p, const char*zSym))(void); -static void apndDlClose(sqlite3_vfs*, void*); -static int apndRandomness(sqlite3_vfs*, int nByte, char *zOut); -static int apndSleep(sqlite3_vfs*, int microseconds); -static int apndCurrentTime(sqlite3_vfs*, double*); -static int apndGetLastError(sqlite3_vfs*, int, char *); -static int apndCurrentTimeInt64(sqlite3_vfs*, sqlite3_int64*); -static int apndSetSystemCall(sqlite3_vfs*, const char*,sqlite3_syscall_ptr); -static sqlite3_syscall_ptr apndGetSystemCall(sqlite3_vfs*, const char *z); -static const char *apndNextSystemCall(sqlite3_vfs*, const char *zName); - -static sqlite3_vfs apnd_vfs = { - 3, /* iVersion (set when registered) */ - 0, /* szOsFile (set when registered) */ - 1024, /* mxPathname */ - 0, /* pNext */ - "apndvfs", /* zName */ - 0, /* pAppData (set when registered) */ - apndOpen, /* xOpen */ - apndDelete, /* xDelete */ - apndAccess, /* xAccess */ - apndFullPathname, /* xFullPathname */ - apndDlOpen, /* xDlOpen */ - apndDlError, /* xDlError */ - apndDlSym, /* xDlSym */ - apndDlClose, /* xDlClose */ - apndRandomness, /* xRandomness */ - apndSleep, /* xSleep */ - apndCurrentTime, /* xCurrentTime */ - apndGetLastError, /* xGetLastError */ - apndCurrentTimeInt64, /* xCurrentTimeInt64 */ - apndSetSystemCall, /* xSetSystemCall */ - apndGetSystemCall, /* xGetSystemCall */ - apndNextSystemCall /* xNextSystemCall */ -}; - -static const sqlite3_io_methods apnd_io_methods = { - 3, /* iVersion */ - apndClose, /* xClose */ - apndRead, /* xRead */ - apndWrite, /* xWrite */ - apndTruncate, /* xTruncate */ - apndSync, /* xSync */ - apndFileSize, /* xFileSize */ - apndLock, /* xLock */ - apndUnlock, /* xUnlock */ - apndCheckReservedLock, /* xCheckReservedLock */ - apndFileControl, /* xFileControl */ - apndSectorSize, /* xSectorSize */ - apndDeviceCharacteristics, /* xDeviceCharacteristics */ - apndShmMap, /* xShmMap */ - apndShmLock, /* xShmLock */ - apndShmBarrier, /* xShmBarrier */ - apndShmUnmap, /* xShmUnmap */ - apndFetch, /* xFetch */ - apndUnfetch /* xUnfetch */ -}; - - - -/* -** Close an apnd-file. -*/ -static int apndClose(sqlite3_file *pFile){ - pFile = ORIGFILE(pFile); - return pFile->pMethods->xClose(pFile); -} - -/* -** Read data from an apnd-file. -*/ -static int apndRead( - sqlite3_file *pFile, - void *zBuf, - int iAmt, - sqlite_int64 iOfst -){ - ApndFile *p = (ApndFile *)pFile; - pFile = ORIGFILE(pFile); - return pFile->pMethods->xRead(pFile, zBuf, iAmt, iOfst+p->iPgOne); -} - -/* -** Add the append-mark onto the end of the file. -*/ -static int apndWriteMark(ApndFile *p, sqlite3_file *pFile){ - int i; - unsigned char a[APND_MARK_SIZE]; - memcpy(a, APND_MARK_PREFIX, APND_MARK_PREFIX_SZ); - for(i=0; i<8; i++){ - a[APND_MARK_PREFIX_SZ+i] = (p->iPgOne >> (56 - i*8)) & 0xff; - } - return pFile->pMethods->xWrite(pFile, a, APND_MARK_SIZE, p->iMark); -} - -/* -** Write data to an apnd-file. -*/ -static int apndWrite( - sqlite3_file *pFile, - const void *zBuf, - int iAmt, - sqlite_int64 iOfst -){ - int rc; - ApndFile *p = (ApndFile *)pFile; - pFile = ORIGFILE(pFile); - if( iOfst+iAmt>=APND_MAX_SIZE ) return SQLITE_FULL; - rc = pFile->pMethods->xWrite(pFile, zBuf, iAmt, iOfst+p->iPgOne); - if( rc==SQLITE_OK && iOfst + iAmt + p->iPgOne > p->iMark ){ - sqlite3_int64 sz = 0; - rc = pFile->pMethods->xFileSize(pFile, &sz); - if( rc==SQLITE_OK ){ - p->iMark = sz - APND_MARK_SIZE; - if( iOfst + iAmt + p->iPgOne > p->iMark ){ - p->iMark = p->iPgOne + iOfst + iAmt; - rc = apndWriteMark(p, pFile); - } - } - } - return rc; -} - -/* -** Truncate an apnd-file. -*/ -static int apndTruncate(sqlite3_file *pFile, sqlite_int64 size){ - int rc; - ApndFile *p = (ApndFile *)pFile; - pFile = ORIGFILE(pFile); - rc = pFile->pMethods->xTruncate(pFile, size+p->iPgOne+APND_MARK_SIZE); - if( rc==SQLITE_OK ){ - p->iMark = p->iPgOne+size; - rc = apndWriteMark(p, pFile); - } - return rc; -} - -/* -** Sync an apnd-file. -*/ -static int apndSync(sqlite3_file *pFile, int flags){ - pFile = ORIGFILE(pFile); - return pFile->pMethods->xSync(pFile, flags); -} - -/* -** Return the current file-size of an apnd-file. -*/ -static int apndFileSize(sqlite3_file *pFile, sqlite_int64 *pSize){ - ApndFile *p = (ApndFile *)pFile; - int rc; - pFile = ORIGFILE(p); - rc = pFile->pMethods->xFileSize(pFile, pSize); - if( rc==SQLITE_OK && p->iPgOne ){ - *pSize -= p->iPgOne + APND_MARK_SIZE; - } - return rc; -} - -/* -** Lock an apnd-file. -*/ -static int apndLock(sqlite3_file *pFile, int eLock){ - pFile = ORIGFILE(pFile); - return pFile->pMethods->xLock(pFile, eLock); -} - -/* -** Unlock an apnd-file. -*/ -static int apndUnlock(sqlite3_file *pFile, int eLock){ - pFile = ORIGFILE(pFile); - return pFile->pMethods->xUnlock(pFile, eLock); -} - -/* -** Check if another file-handle holds a RESERVED lock on an apnd-file. -*/ -static int apndCheckReservedLock(sqlite3_file *pFile, int *pResOut){ - pFile = ORIGFILE(pFile); - return pFile->pMethods->xCheckReservedLock(pFile, pResOut); -} - -/* -** File control method. For custom operations on an apnd-file. -*/ -static int apndFileControl(sqlite3_file *pFile, int op, void *pArg){ - ApndFile *p = (ApndFile *)pFile; - int rc; - pFile = ORIGFILE(pFile); - rc = pFile->pMethods->xFileControl(pFile, op, pArg); - if( rc==SQLITE_OK && op==SQLITE_FCNTL_VFSNAME ){ - *(char**)pArg = sqlite3_mprintf("apnd(%lld)/%z", p->iPgOne, *(char**)pArg); - } - return rc; -} - -/* -** Return the sector-size in bytes for an apnd-file. -*/ -static int apndSectorSize(sqlite3_file *pFile){ - pFile = ORIGFILE(pFile); - return pFile->pMethods->xSectorSize(pFile); -} - -/* -** Return the device characteristic flags supported by an apnd-file. -*/ -static int apndDeviceCharacteristics(sqlite3_file *pFile){ - pFile = ORIGFILE(pFile); - return pFile->pMethods->xDeviceCharacteristics(pFile); -} - -/* Create a shared memory file mapping */ -static int apndShmMap( - sqlite3_file *pFile, - int iPg, - int pgsz, - int bExtend, - void volatile **pp -){ - pFile = ORIGFILE(pFile); - return pFile->pMethods->xShmMap(pFile,iPg,pgsz,bExtend,pp); -} - -/* Perform locking on a shared-memory segment */ -static int apndShmLock(sqlite3_file *pFile, int offset, int n, int flags){ - pFile = ORIGFILE(pFile); - return pFile->pMethods->xShmLock(pFile,offset,n,flags); -} - -/* Memory barrier operation on shared memory */ -static void apndShmBarrier(sqlite3_file *pFile){ - pFile = ORIGFILE(pFile); - pFile->pMethods->xShmBarrier(pFile); -} - -/* Unmap a shared memory segment */ -static int apndShmUnmap(sqlite3_file *pFile, int deleteFlag){ - pFile = ORIGFILE(pFile); - return pFile->pMethods->xShmUnmap(pFile,deleteFlag); -} - -/* Fetch a page of a memory-mapped file */ -static int apndFetch( - sqlite3_file *pFile, - sqlite3_int64 iOfst, - int iAmt, - void **pp -){ - ApndFile *p = (ApndFile *)pFile; - pFile = ORIGFILE(pFile); - return pFile->pMethods->xFetch(pFile, iOfst+p->iPgOne, iAmt, pp); -} - -/* Release a memory-mapped page */ -static int apndUnfetch(sqlite3_file *pFile, sqlite3_int64 iOfst, void *pPage){ - ApndFile *p = (ApndFile *)pFile; - pFile = ORIGFILE(pFile); - return pFile->pMethods->xUnfetch(pFile, iOfst+p->iPgOne, pPage); -} - -/* -** Check to see if the file is an ordinary SQLite database file. -*/ -static int apndIsOrdinaryDatabaseFile(sqlite3_int64 sz, sqlite3_file *pFile){ - int rc; - char zHdr[16]; - static const char aSqliteHdr[] = "SQLite format 3"; - if( sz<512 ) return 0; - rc = pFile->pMethods->xRead(pFile, zHdr, sizeof(zHdr), 0); - if( rc ) return 0; - return memcmp(zHdr, aSqliteHdr, sizeof(zHdr))==0; -} - -/* -** Try to read the append-mark off the end of a file. Return the -** start of the appended database if the append-mark is present. If -** there is no append-mark, return -1; -*/ -static sqlite3_int64 apndReadMark(sqlite3_int64 sz, sqlite3_file *pFile){ - int rc, i; - sqlite3_int64 iMark; - unsigned char a[APND_MARK_SIZE]; - - if( sz<=APND_MARK_SIZE ) return -1; - rc = pFile->pMethods->xRead(pFile, a, APND_MARK_SIZE, sz-APND_MARK_SIZE); - if( rc ) return -1; - if( memcmp(a, APND_MARK_PREFIX, APND_MARK_PREFIX_SZ)!=0 ) return -1; - iMark = ((sqlite3_int64)(a[APND_MARK_PREFIX_SZ]&0x7f))<<56; - for(i=1; i<8; i++){ - iMark += (sqlite3_int64)a[APND_MARK_PREFIX_SZ+i]<<(56-8*i); - } - return iMark; -} - -/* -** Open an apnd file handle. -*/ -static int apndOpen( - sqlite3_vfs *pVfs, - const char *zName, - sqlite3_file *pFile, - int flags, - int *pOutFlags -){ - ApndFile *p; - sqlite3_file *pSubFile; - sqlite3_vfs *pSubVfs; - int rc; - sqlite3_int64 sz; - pSubVfs = ORIGVFS(pVfs); - if( (flags & SQLITE_OPEN_MAIN_DB)==0 ){ - return pSubVfs->xOpen(pSubVfs, zName, pFile, flags, pOutFlags); - } - p = (ApndFile*)pFile; - memset(p, 0, sizeof(*p)); - pSubFile = ORIGFILE(pFile); - pFile->pMethods = &apnd_io_methods; - rc = pSubVfs->xOpen(pSubVfs, zName, pSubFile, flags, pOutFlags); - if( rc ) goto apnd_open_done; - rc = pSubFile->pMethods->xFileSize(pSubFile, &sz); - if( rc ){ - pSubFile->pMethods->xClose(pSubFile); - goto apnd_open_done; - } - if( apndIsOrdinaryDatabaseFile(sz, pSubFile) ){ - memmove(pFile, pSubFile, pSubVfs->szOsFile); - return SQLITE_OK; - } - p->iMark = 0; - p->iPgOne = apndReadMark(sz, pFile); - if( p->iPgOne>0 ){ - return SQLITE_OK; - } - if( (flags & SQLITE_OPEN_CREATE)==0 ){ - pSubFile->pMethods->xClose(pSubFile); - rc = SQLITE_CANTOPEN; - } - p->iPgOne = (sz+0xfff) & ~(sqlite3_int64)0xfff; -apnd_open_done: - if( rc ) pFile->pMethods = 0; - return rc; -} - -/* -** All other VFS methods are pass-thrus. -*/ -static int apndDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){ - return ORIGVFS(pVfs)->xDelete(ORIGVFS(pVfs), zPath, dirSync); -} -static int apndAccess( - sqlite3_vfs *pVfs, - const char *zPath, - int flags, - int *pResOut -){ - return ORIGVFS(pVfs)->xAccess(ORIGVFS(pVfs), zPath, flags, pResOut); -} -static int apndFullPathname( - sqlite3_vfs *pVfs, - const char *zPath, - int nOut, - char *zOut -){ - return ORIGVFS(pVfs)->xFullPathname(ORIGVFS(pVfs),zPath,nOut,zOut); -} -static void *apndDlOpen(sqlite3_vfs *pVfs, const char *zPath){ - return ORIGVFS(pVfs)->xDlOpen(ORIGVFS(pVfs), zPath); -} -static void apndDlError(sqlite3_vfs *pVfs, int nByte, char *zErrMsg){ - ORIGVFS(pVfs)->xDlError(ORIGVFS(pVfs), nByte, zErrMsg); -} -static void (*apndDlSym(sqlite3_vfs *pVfs, void *p, const char *zSym))(void){ - return ORIGVFS(pVfs)->xDlSym(ORIGVFS(pVfs), p, zSym); -} -static void apndDlClose(sqlite3_vfs *pVfs, void *pHandle){ - ORIGVFS(pVfs)->xDlClose(ORIGVFS(pVfs), pHandle); -} -static int apndRandomness(sqlite3_vfs *pVfs, int nByte, char *zBufOut){ - return ORIGVFS(pVfs)->xRandomness(ORIGVFS(pVfs), nByte, zBufOut); -} -static int apndSleep(sqlite3_vfs *pVfs, int nMicro){ - return ORIGVFS(pVfs)->xSleep(ORIGVFS(pVfs), nMicro); -} -static int apndCurrentTime(sqlite3_vfs *pVfs, double *pTimeOut){ - return ORIGVFS(pVfs)->xCurrentTime(ORIGVFS(pVfs), pTimeOut); -} -static int apndGetLastError(sqlite3_vfs *pVfs, int a, char *b){ - return ORIGVFS(pVfs)->xGetLastError(ORIGVFS(pVfs), a, b); -} -static int apndCurrentTimeInt64(sqlite3_vfs *pVfs, sqlite3_int64 *p){ - return ORIGVFS(pVfs)->xCurrentTimeInt64(ORIGVFS(pVfs), p); -} -static int apndSetSystemCall( - sqlite3_vfs *pVfs, - const char *zName, - sqlite3_syscall_ptr pCall -){ - return ORIGVFS(pVfs)->xSetSystemCall(ORIGVFS(pVfs),zName,pCall); -} -static sqlite3_syscall_ptr apndGetSystemCall( - sqlite3_vfs *pVfs, - const char *zName -){ - return ORIGVFS(pVfs)->xGetSystemCall(ORIGVFS(pVfs),zName); -} -static const char *apndNextSystemCall(sqlite3_vfs *pVfs, const char *zName){ - return ORIGVFS(pVfs)->xNextSystemCall(ORIGVFS(pVfs), zName); -} - - -#ifdef _WIN32 - -#endif -/* -** This routine is called when the extension is loaded. -** Register the new VFS. -*/ -int sqlite3_appendvfs_init( - sqlite3 *db, - char **pzErrMsg, - const sqlite3_api_routines *pApi -){ - int rc = SQLITE_OK; - sqlite3_vfs *pOrig; - SQLITE_EXTENSION_INIT2(pApi); - (void)pzErrMsg; - (void)db; - pOrig = sqlite3_vfs_find(0); - apnd_vfs.iVersion = pOrig->iVersion; - apnd_vfs.pAppData = pOrig; - apnd_vfs.szOsFile = pOrig->szOsFile + sizeof(ApndFile); - rc = sqlite3_vfs_register(&apnd_vfs, 0); -#ifdef APPENDVFS_TEST - if( rc==SQLITE_OK ){ - rc = sqlite3_auto_extension((void(*)(void))apndvfsRegister); - } -#endif - if( rc==SQLITE_OK ) rc = SQLITE_OK_LOAD_PERMANENTLY; - return rc; -} - -/************************* End ../ext/misc/appendvfs.c ********************/ -/************************* Begin ../ext/misc/memtrace.c ******************/ -/* -** 2019-01-21 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** -** This file implements an extension that uses the SQLITE_CONFIG_MALLOC -** mechanism to add a tracing layer on top of SQLite. If this extension -** is registered prior to sqlite3_initialize(), it will cause all memory -** allocation activities to be logged on standard output, or to some other -** FILE specified by the initializer. -** -** This file needs to be compiled into the application that uses it. -** -** This extension is used to implement the --memtrace option of the -** command-line shell. -*/ -#include -#include -#include - -/* The original memory allocation routines */ -static sqlite3_mem_methods memtraceBase; -static FILE *memtraceOut; - -/* Methods that trace memory allocations */ -static void *memtraceMalloc(int n){ - if( memtraceOut ){ - fprintf(memtraceOut, "MEMTRACE: allocate %d bytes\n", - memtraceBase.xRoundup(n)); - } - return memtraceBase.xMalloc(n); -} -static void memtraceFree(void *p){ - if( p==0 ) return; - if( memtraceOut ){ - fprintf(memtraceOut, "MEMTRACE: free %d bytes\n", memtraceBase.xSize(p)); - } - memtraceBase.xFree(p); -} -static void *memtraceRealloc(void *p, int n){ - if( p==0 ) return memtraceMalloc(n); - if( n==0 ){ - memtraceFree(p); - return 0; - } - if( memtraceOut ){ - fprintf(memtraceOut, "MEMTRACE: resize %d -> %d bytes\n", - memtraceBase.xSize(p), memtraceBase.xRoundup(n)); - } - return memtraceBase.xRealloc(p, n); -} -static int memtraceSize(void *p){ - return memtraceBase.xSize(p); -} -static int memtraceRoundup(int n){ - return memtraceBase.xRoundup(n); -} -static int memtraceInit(void *p){ - return memtraceBase.xInit(p); -} -static void memtraceShutdown(void *p){ - memtraceBase.xShutdown(p); -} - -/* The substitute memory allocator */ -static sqlite3_mem_methods ersaztMethods = { - memtraceMalloc, - memtraceFree, - memtraceRealloc, - memtraceSize, - memtraceRoundup, - memtraceInit, - memtraceShutdown, - 0 -}; - -/* Begin tracing memory allocations to out. */ -int sqlite3MemTraceActivate(FILE *out){ - int rc = SQLITE_OK; - if( memtraceBase.xMalloc==0 ){ - rc = sqlite3_config(SQLITE_CONFIG_GETMALLOC, &memtraceBase); - if( rc==SQLITE_OK ){ - rc = sqlite3_config(SQLITE_CONFIG_MALLOC, &ersaztMethods); - } - } - memtraceOut = out; - return rc; -} - -/* Deactivate memory tracing */ -int sqlite3MemTraceDeactivate(void){ - int rc = SQLITE_OK; - if( memtraceBase.xMalloc!=0 ){ - rc = sqlite3_config(SQLITE_CONFIG_MALLOC, &memtraceBase); - if( rc==SQLITE_OK ){ - memset(&memtraceBase, 0, sizeof(memtraceBase)); - } - } - memtraceOut = 0; - return rc; -} - -/************************* End ../ext/misc/memtrace.c ********************/ -/************************* Begin ../ext/misc/uint.c ******************/ -/* -** 2020-04-14 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -****************************************************************************** -** -** This SQLite extension implements the UINT collating sequence. -** -** UINT works like BINARY for text, except that embedded strings -** of digits compare in numeric order. -** -** * Leading zeros are handled properly, in the sense that -** they do not mess of the maginitude comparison of embedded -** strings of digits. "x00123y" is equal to "x123y". -** -** * Only unsigned integers are recognized. Plus and minus -** signs are ignored. Decimal points and exponential notation -** are ignored. -** -** * Embedded integers can be of arbitrary length. Comparison -** is *not* limited integers that can be expressed as a -** 64-bit machine integer. -*/ -/* #include "sqlite3ext.h" */ -SQLITE_EXTENSION_INIT1 -#include -#include -#include - -/* -** Compare text in lexicographic order, except strings of digits -** compare in numeric order. -*/ -static int uintCollFunc( - void *notUsed, - int nKey1, const void *pKey1, - int nKey2, const void *pKey2 -){ - const unsigned char *zA = (const unsigned char*)pKey1; - const unsigned char *zB = (const unsigned char*)pKey2; - int i=0, j=0, x; - (void)notUsed; - while( i -#include -#include -#include - -/* Mark a function parameter as unused, to suppress nuisance compiler -** warnings. */ -#ifndef UNUSED_PARAMETER -# define UNUSED_PARAMETER(X) (void)(X) -#endif - - -/* A decimal object */ -typedef struct Decimal Decimal; -struct Decimal { - char sign; /* 0 for positive, 1 for negative */ - char oom; /* True if an OOM is encountered */ - char isNull; /* True if holds a NULL rather than a number */ - char isInit; /* True upon initialization */ - int nDigit; /* Total number of digits */ - int nFrac; /* Number of digits to the right of the decimal point */ - signed char *a; /* Array of digits. Most significant first. */ -}; - -/* -** Release memory held by a Decimal, but do not free the object itself. -*/ -static void decimal_clear(Decimal *p){ - sqlite3_free(p->a); -} - -/* -** Destroy a Decimal object -*/ -static void decimal_free(Decimal *p){ - if( p ){ - decimal_clear(p); - sqlite3_free(p); - } -} - -/* -** Allocate a new Decimal object. Initialize it to the number given -** by the input string. -*/ -static Decimal *decimal_new( - sqlite3_context *pCtx, - sqlite3_value *pIn, - int nAlt, - const unsigned char *zAlt -){ - Decimal *p; - int n, i; - const unsigned char *zIn; - int iExp = 0; - p = sqlite3_malloc( sizeof(*p) ); - if( p==0 ) goto new_no_mem; - p->sign = 0; - p->oom = 0; - p->isInit = 1; - p->isNull = 0; - p->nDigit = 0; - p->nFrac = 0; - if( zAlt ){ - n = nAlt, - zIn = zAlt; - }else{ - if( sqlite3_value_type(pIn)==SQLITE_NULL ){ - p->a = 0; - p->isNull = 1; - return p; - } - n = sqlite3_value_bytes(pIn); - zIn = sqlite3_value_text(pIn); - } - p->a = sqlite3_malloc64( n+1 ); - if( p->a==0 ) goto new_no_mem; - for(i=0; isspace(zIn[i]); i++){} - if( zIn[i]=='-' ){ - p->sign = 1; - i++; - }else if( zIn[i]=='+' ){ - i++; - } - while( i='0' && c<='9' ){ - p->a[p->nDigit++] = c - '0'; - }else if( c=='.' ){ - p->nFrac = p->nDigit + 1; - }else if( c=='e' || c=='E' ){ - int j = i+1; - int neg = 0; - if( j>=n ) break; - if( zIn[j]=='-' ){ - neg = 1; - j++; - }else if( zIn[j]=='+' ){ - j++; - } - while( j='0' && zIn[j]<='9' ){ - iExp = iExp*10 + zIn[j] - '0'; - } - j++; - } - if( neg ) iExp = -iExp; - break; - } - i++; - } - if( p->nFrac ){ - p->nFrac = p->nDigit - (p->nFrac - 1); - } - if( iExp>0 ){ - if( p->nFrac>0 ){ - if( iExp<=p->nFrac ){ - p->nFrac -= iExp; - iExp = 0; - }else{ - iExp -= p->nFrac; - p->nFrac = 0; - } - } - if( iExp>0 ){ - p->a = sqlite3_realloc64(p->a, p->nDigit + iExp + 1 ); - if( p->a==0 ) goto new_no_mem; - memset(p->a+p->nDigit, 0, iExp); - p->nDigit += iExp; - } - }else if( iExp<0 ){ - int nExtra; - iExp = -iExp; - nExtra = p->nDigit - p->nFrac - 1; - if( nExtra ){ - if( nExtra>=iExp ){ - p->nFrac += iExp; - iExp = 0; - }else{ - iExp -= nExtra; - p->nFrac = p->nDigit - 1; - } - } - if( iExp>0 ){ - p->a = sqlite3_realloc64(p->a, p->nDigit + iExp + 1 ); - if( p->a==0 ) goto new_no_mem; - memmove(p->a+iExp, p->a, p->nDigit); - memset(p->a, 0, iExp); - p->nDigit += iExp; - p->nFrac += iExp; - } - } - return p; - -new_no_mem: - if( pCtx ) sqlite3_result_error_nomem(pCtx); - sqlite3_free(p); - return 0; -} - -/* -** Make the given Decimal the result. -*/ -static void decimal_result(sqlite3_context *pCtx, Decimal *p){ - char *z; - int i, j; - int n; - if( p==0 || p->oom ){ - sqlite3_result_error_nomem(pCtx); - return; - } - if( p->isNull ){ - sqlite3_result_null(pCtx); - return; - } - z = sqlite3_malloc( p->nDigit+4 ); - if( z==0 ){ - sqlite3_result_error_nomem(pCtx); - return; - } - i = 0; - if( p->nDigit==0 || (p->nDigit==1 && p->a[0]==0) ){ - p->sign = 0; - } - if( p->sign ){ - z[0] = '-'; - i = 1; - } - n = p->nDigit - p->nFrac; - if( n<=0 ){ - z[i++] = '0'; - } - j = 0; - while( n>1 && p->a[j]==0 ){ - j++; - n--; - } - while( n>0 ){ - z[i++] = p->a[j] + '0'; - j++; - n--; - } - if( p->nFrac ){ - z[i++] = '.'; - do{ - z[i++] = p->a[j] + '0'; - j++; - }while( jnDigit ); - } - z[i] = 0; - sqlite3_result_text(pCtx, z, i, sqlite3_free); -} - -/* -** SQL Function: decimal(X) -** -** Convert input X into decimal and then back into text -*/ -static void decimalFunc( - sqlite3_context *context, - int argc, - sqlite3_value **argv -){ - Decimal *p = decimal_new(context, argv[0], 0, 0); - UNUSED_PARAMETER(argc); - decimal_result(context, p); - decimal_free(p); -} - -/* -** Compare to Decimal objects. Return negative, 0, or positive if the -** first object is less than, equal to, or greater than the second. -** -** Preconditions for this routine: -** -** pA!=0 -** pA->isNull==0 -** pB!=0 -** pB->isNull==0 -*/ -static int decimal_cmp(const Decimal *pA, const Decimal *pB){ - int nASig, nBSig, rc, n; - if( pA->sign!=pB->sign ){ - return pA->sign ? -1 : +1; - } - if( pA->sign ){ - const Decimal *pTemp = pA; - pA = pB; - pB = pTemp; - } - nASig = pA->nDigit - pA->nFrac; - nBSig = pB->nDigit - pB->nFrac; - if( nASig!=nBSig ){ - return nASig - nBSig; - } - n = pA->nDigit; - if( n>pB->nDigit ) n = pB->nDigit; - rc = memcmp(pA->a, pB->a, n); - if( rc==0 ){ - rc = pA->nDigit - pB->nDigit; - } - return rc; -} - -/* -** SQL Function: decimal_cmp(X, Y) -** -** Return negative, zero, or positive if X is less then, equal to, or -** greater than Y. -*/ -static void decimalCmpFunc( - sqlite3_context *context, - int argc, - sqlite3_value **argv -){ - Decimal *pA = 0, *pB = 0; - int rc; - - UNUSED_PARAMETER(argc); - pA = decimal_new(context, argv[0], 0, 0); - if( pA==0 || pA->isNull ) goto cmp_done; - pB = decimal_new(context, argv[1], 0, 0); - if( pB==0 || pB->isNull ) goto cmp_done; - rc = decimal_cmp(pA, pB); - if( rc<0 ) rc = -1; - else if( rc>0 ) rc = +1; - sqlite3_result_int(context, rc); -cmp_done: - decimal_free(pA); - decimal_free(pB); -} - -/* -** Expand the Decimal so that it has a least nDigit digits and nFrac -** digits to the right of the decimal point. -*/ -static void decimal_expand(Decimal *p, int nDigit, int nFrac){ - int nAddSig; - int nAddFrac; - if( p==0 ) return; - nAddFrac = nFrac - p->nFrac; - nAddSig = (nDigit - p->nDigit) - nAddFrac; - if( nAddFrac==0 && nAddSig==0 ) return; - p->a = sqlite3_realloc64(p->a, nDigit+1); - if( p->a==0 ){ - p->oom = 1; - return; - } - if( nAddSig ){ - memmove(p->a+nAddSig, p->a, p->nDigit); - memset(p->a, 0, nAddSig); - p->nDigit += nAddSig; - } - if( nAddFrac ){ - memset(p->a+p->nDigit, 0, nAddFrac); - p->nDigit += nAddFrac; - p->nFrac += nAddFrac; - } -} - -/* -** Add the value pB into pA. -** -** Both pA and pB might become denormalized by this routine. -*/ -static void decimal_add(Decimal *pA, Decimal *pB){ - int nSig, nFrac, nDigit; - int i, rc; - if( pA==0 ){ - return; - } - if( pA->oom || pB==0 || pB->oom ){ - pA->oom = 1; - return; - } - if( pA->isNull || pB->isNull ){ - pA->isNull = 1; - return; - } - nSig = pA->nDigit - pA->nFrac; - if( nSig && pA->a[0]==0 ) nSig--; - if( nSignDigit-pB->nFrac ){ - nSig = pB->nDigit - pB->nFrac; - } - nFrac = pA->nFrac; - if( nFracnFrac ) nFrac = pB->nFrac; - nDigit = nSig + nFrac + 1; - decimal_expand(pA, nDigit, nFrac); - decimal_expand(pB, nDigit, nFrac); - if( pA->oom || pB->oom ){ - pA->oom = 1; - }else{ - if( pA->sign==pB->sign ){ - int carry = 0; - for(i=nDigit-1; i>=0; i--){ - int x = pA->a[i] + pB->a[i] + carry; - if( x>=10 ){ - carry = 1; - pA->a[i] = x - 10; - }else{ - carry = 0; - pA->a[i] = x; - } - } - }else{ - signed char *aA, *aB; - int borrow = 0; - rc = memcmp(pA->a, pB->a, nDigit); - if( rc<0 ){ - aA = pB->a; - aB = pA->a; - pA->sign = !pA->sign; - }else{ - aA = pA->a; - aB = pB->a; - } - for(i=nDigit-1; i>=0; i--){ - int x = aA[i] - aB[i] - borrow; - if( x<0 ){ - pA->a[i] = x+10; - borrow = 1; - }else{ - pA->a[i] = x; - borrow = 0; - } - } - } - } -} - -/* -** Compare text in decimal order. -*/ -static int decimalCollFunc( - void *notUsed, - int nKey1, const void *pKey1, - int nKey2, const void *pKey2 -){ - const unsigned char *zA = (const unsigned char*)pKey1; - const unsigned char *zB = (const unsigned char*)pKey2; - Decimal *pA = decimal_new(0, 0, nKey1, zA); - Decimal *pB = decimal_new(0, 0, nKey2, zB); - int rc; - UNUSED_PARAMETER(notUsed); - if( pA==0 || pB==0 ){ - rc = 0; - }else{ - rc = decimal_cmp(pA, pB); - } - decimal_free(pA); - decimal_free(pB); - return rc; -} - - -/* -** SQL Function: decimal_add(X, Y) -** decimal_sub(X, Y) -** -** Return the sum or difference of X and Y. -*/ -static void decimalAddFunc( - sqlite3_context *context, - int argc, - sqlite3_value **argv -){ - Decimal *pA = decimal_new(context, argv[0], 0, 0); - Decimal *pB = decimal_new(context, argv[1], 0, 0); - UNUSED_PARAMETER(argc); - decimal_add(pA, pB); - decimal_result(context, pA); - decimal_free(pA); - decimal_free(pB); -} -static void decimalSubFunc( - sqlite3_context *context, - int argc, - sqlite3_value **argv -){ - Decimal *pA = decimal_new(context, argv[0], 0, 0); - Decimal *pB = decimal_new(context, argv[1], 0, 0); - UNUSED_PARAMETER(argc); - if( pB==0 ) return; - pB->sign = !pB->sign; - decimal_add(pA, pB); - decimal_result(context, pA); - decimal_free(pA); - decimal_free(pB); -} - -/* Aggregate funcion: decimal_sum(X) -** -** Works like sum() except that it uses decimal arithmetic for unlimited -** precision. -*/ -static void decimalSumStep( - sqlite3_context *context, - int argc, - sqlite3_value **argv -){ - Decimal *p; - Decimal *pArg; - UNUSED_PARAMETER(argc); - p = sqlite3_aggregate_context(context, sizeof(*p)); - if( p==0 ) return; - if( !p->isInit ){ - p->isInit = 1; - p->a = sqlite3_malloc(2); - if( p->a==0 ){ - p->oom = 1; - }else{ - p->a[0] = 0; - } - p->nDigit = 1; - p->nFrac = 0; - } - if( sqlite3_value_type(argv[0])==SQLITE_NULL ) return; - pArg = decimal_new(context, argv[0], 0, 0); - decimal_add(p, pArg); - decimal_free(pArg); -} -static void decimalSumInverse( - sqlite3_context *context, - int argc, - sqlite3_value **argv -){ - Decimal *p; - Decimal *pArg; - UNUSED_PARAMETER(argc); - p = sqlite3_aggregate_context(context, sizeof(*p)); - if( p==0 ) return; - if( sqlite3_value_type(argv[0])==SQLITE_NULL ) return; - pArg = decimal_new(context, argv[0], 0, 0); - if( pArg ) pArg->sign = !pArg->sign; - decimal_add(p, pArg); - decimal_free(pArg); -} -static void decimalSumValue(sqlite3_context *context){ - Decimal *p = sqlite3_aggregate_context(context, 0); - if( p==0 ) return; - decimal_result(context, p); -} -static void decimalSumFinalize(sqlite3_context *context){ - Decimal *p = sqlite3_aggregate_context(context, 0); - if( p==0 ) return; - decimal_result(context, p); - decimal_clear(p); -} - -/* -** SQL Function: decimal_mul(X, Y) -** -** Return the product of X and Y. -** -** All significant digits after the decimal point are retained. -** Trailing zeros after the decimal point are omitted as long as -** the number of digits after the decimal point is no less than -** either the number of digits in either input. -*/ -static void decimalMulFunc( - sqlite3_context *context, - int argc, - sqlite3_value **argv -){ - Decimal *pA = decimal_new(context, argv[0], 0, 0); - Decimal *pB = decimal_new(context, argv[1], 0, 0); - signed char *acc = 0; - int i, j, k; - int minFrac; - UNUSED_PARAMETER(argc); - if( pA==0 || pA->oom || pA->isNull - || pB==0 || pB->oom || pB->isNull - ){ - goto mul_end; - } - acc = sqlite3_malloc64( pA->nDigit + pB->nDigit + 2 ); - if( acc==0 ){ - sqlite3_result_error_nomem(context); - goto mul_end; - } - memset(acc, 0, pA->nDigit + pB->nDigit + 2); - minFrac = pA->nFrac; - if( pB->nFracnFrac; - for(i=pA->nDigit-1; i>=0; i--){ - signed char f = pA->a[i]; - int carry = 0, x; - for(j=pB->nDigit-1, k=i+j+3; j>=0; j--, k--){ - x = acc[k] + f*pB->a[j] + carry; - acc[k] = x%10; - carry = x/10; - } - x = acc[k] + carry; - acc[k] = x%10; - acc[k-1] += x/10; - } - sqlite3_free(pA->a); - pA->a = acc; - acc = 0; - pA->nDigit += pB->nDigit + 2; - pA->nFrac += pB->nFrac; - pA->sign ^= pB->sign; - while( pA->nFrac>minFrac && pA->a[pA->nDigit-1]==0 ){ - pA->nFrac--; - pA->nDigit--; - } - decimal_result(context, pA); - -mul_end: - sqlite3_free(acc); - decimal_free(pA); - decimal_free(pB); -} - -#ifdef _WIN32 - -#endif -int sqlite3_decimal_init( - sqlite3 *db, - char **pzErrMsg, - const sqlite3_api_routines *pApi -){ - int rc = SQLITE_OK; - static const struct { - const char *zFuncName; - int nArg; - void (*xFunc)(sqlite3_context*,int,sqlite3_value**); - } aFunc[] = { - { "decimal", 1, decimalFunc }, - { "decimal_cmp", 2, decimalCmpFunc }, - { "decimal_add", 2, decimalAddFunc }, - { "decimal_sub", 2, decimalSubFunc }, - { "decimal_mul", 2, decimalMulFunc }, - }; - unsigned int i; - (void)pzErrMsg; /* Unused parameter */ - - SQLITE_EXTENSION_INIT2(pApi); - - for(i=0; i 'ieee754(2,0)' -** ieee754(45.25) -> 'ieee754(181,-2)' -** ieee754(2, 0) -> 2.0 -** ieee754(181, -2) -> 45.25 -** -** Two additional functions break apart the one-argument ieee754() -** result into separate integer values: -** -** ieee754_mantissa(45.25) -> 181 -** ieee754_exponent(45.25) -> -2 -** -** These functions convert binary64 numbers into blobs and back again. -** -** ieee754_from_blob(x'3ff0000000000000') -> 1.0 -** ieee754_to_blob(1.0) -> x'3ff0000000000000' -** -** In all single-argument functions, if the argument is an 8-byte blob -** then that blob is interpreted as a big-endian binary64 value. -** -** -** EXACT DECIMAL REPRESENTATION OF BINARY64 VALUES -** ----------------------------------------------- -** -** This extension in combination with the separate 'decimal' extension -** can be used to compute the exact decimal representation of binary64 -** values. To begin, first compute a table of exponent values: -** -** CREATE TABLE pow2(x INTEGER PRIMARY KEY, v TEXT); -** WITH RECURSIVE c(x,v) AS ( -** VALUES(0,'1') -** UNION ALL -** SELECT x+1, decimal_mul(v,'2') FROM c WHERE x+1<=971 -** ) INSERT INTO pow2(x,v) SELECT x, v FROM c; -** WITH RECURSIVE c(x,v) AS ( -** VALUES(-1,'0.5') -** UNION ALL -** SELECT x-1, decimal_mul(v,'0.5') FROM c WHERE x-1>=-1075 -** ) INSERT INTO pow2(x,v) SELECT x, v FROM c; -** -** Then, to compute the exact decimal representation of a floating -** point value (the value 47.49 is used in the example) do: -** -** WITH c(n) AS (VALUES(47.49)) -** ---------------^^^^^---- Replace with whatever you want -** SELECT decimal_mul(ieee754_mantissa(c.n),pow2.v) -** FROM pow2, c WHERE pow2.x=ieee754_exponent(c.n); -** -** Here is a query to show various boundry values for the binary64 -** number format: -** -** WITH c(name,bin) AS (VALUES -** ('minimum positive value', x'0000000000000001'), -** ('maximum subnormal value', x'000fffffffffffff'), -** ('mininum positive nornal value', x'0010000000000000'), -** ('maximum value', x'7fefffffffffffff')) -** SELECT c.name, decimal_mul(ieee754_mantissa(c.bin),pow2.v) -** FROM pow2, c WHERE pow2.x=ieee754_exponent(c.bin); -** -*/ -/* #include "sqlite3ext.h" */ -SQLITE_EXTENSION_INIT1 -#include -#include - -/* Mark a function parameter as unused, to suppress nuisance compiler -** warnings. */ -#ifndef UNUSED_PARAMETER -# define UNUSED_PARAMETER(X) (void)(X) -#endif - -/* -** Implementation of the ieee754() function -*/ -static void ieee754func( - sqlite3_context *context, - int argc, - sqlite3_value **argv -){ - if( argc==1 ){ - sqlite3_int64 m, a; - double r; - int e; - int isNeg; - char zResult[100]; - assert( sizeof(m)==sizeof(r) ); - if( sqlite3_value_type(argv[0])==SQLITE_BLOB - && sqlite3_value_bytes(argv[0])==sizeof(r) - ){ - const unsigned char *x = sqlite3_value_blob(argv[0]); - unsigned int i; - sqlite3_uint64 v = 0; - for(i=0; i>52; - m = a & ((((sqlite3_int64)1)<<52)-1); - if( e==0 ){ - m <<= 1; - }else{ - m |= ((sqlite3_int64)1)<<52; - } - while( e<1075 && m>0 && (m&1)==0 ){ - m >>= 1; - e++; - } - if( isNeg ) m = -m; - } - switch( *(int*)sqlite3_user_data(context) ){ - case 0: - sqlite3_snprintf(sizeof(zResult), zResult, "ieee754(%lld,%d)", - m, e-1075); - sqlite3_result_text(context, zResult, -1, SQLITE_TRANSIENT); - break; - case 1: - sqlite3_result_int64(context, m); - break; - case 2: - sqlite3_result_int(context, e-1075); - break; - } - }else{ - sqlite3_int64 m, e, a; - double r; - int isNeg = 0; - m = sqlite3_value_int64(argv[0]); - e = sqlite3_value_int64(argv[1]); - if( m<0 ){ - isNeg = 1; - m = -m; - if( m<0 ) return; - }else if( m==0 && e>-1000 && e<1000 ){ - sqlite3_result_double(context, 0.0); - return; - } - while( (m>>32)&0xffe00000 ){ - m >>= 1; - e++; - } - while( m!=0 && ((m>>32)&0xfff00000)==0 ){ - m <<= 1; - e--; - } - e += 1075; - if( e<=0 ){ - /* Subnormal */ - m >>= 1-e; - e = 0; - }else if( e>0x7ff ){ - e = 0x7ff; - } - a = m & ((((sqlite3_int64)1)<<52)-1); - a |= e<<52; - if( isNeg ) a |= ((sqlite3_uint64)1)<<63; - memcpy(&r, &a, sizeof(r)); - sqlite3_result_double(context, r); - } -} - -/* -** Functions to convert between blobs and floats. -*/ -static void ieee754func_from_blob( - sqlite3_context *context, - int argc, - sqlite3_value **argv -){ - UNUSED_PARAMETER(argc); - if( sqlite3_value_type(argv[0])==SQLITE_BLOB - && sqlite3_value_bytes(argv[0])==sizeof(double) - ){ - double r; - const unsigned char *x = sqlite3_value_blob(argv[0]); - unsigned int i; - sqlite3_uint64 v = 0; - for(i=0; i>= 8; - } - sqlite3_result_blob(context, a, sizeof(r), SQLITE_TRANSIENT); - } -} - - -#ifdef _WIN32 - -#endif -int sqlite3_ieee_init( - sqlite3 *db, - char **pzErrMsg, - const sqlite3_api_routines *pApi -){ - static const struct { - char *zFName; - int nArg; - int iAux; - void (*xFunc)(sqlite3_context*,int,sqlite3_value**); - } aFunc[] = { - { "ieee754", 1, 0, ieee754func }, - { "ieee754", 2, 0, ieee754func }, - { "ieee754_mantissa", 1, 1, ieee754func }, - { "ieee754_exponent", 1, 2, ieee754func }, - { "ieee754_to_blob", 1, 0, ieee754func_to_blob }, - { "ieee754_from_blob", 1, 0, ieee754func_from_blob }, - - }; - unsigned int i; - int rc = SQLITE_OK; - SQLITE_EXTENSION_INIT2(pApi); - (void)pzErrMsg; /* Unused parameter */ - for(i=0; i -#include -#include - -#include - -#ifndef SQLITE_OMIT_VIRTUALTABLE - -#ifndef SQLITE_AMALGAMATION - -/* typedef sqlite3_int64 i64; */ -/* typedef unsigned char u8; */ -typedef unsigned short u16; -typedef unsigned long u32; -#define MIN(a,b) ((a)<(b) ? (a) : (b)) - -#if defined(SQLITE_COVERAGE_TEST) || defined(SQLITE_MUTATION_TEST) -# define ALWAYS(X) (1) -# define NEVER(X) (0) -#elif !defined(NDEBUG) -# define ALWAYS(X) ((X)?1:(assert(0),0)) -# define NEVER(X) ((X)?(assert(0),1):0) -#else -# define ALWAYS(X) (X) -# define NEVER(X) (X) -#endif - -#endif /* SQLITE_AMALGAMATION */ - -/* -** Definitions for mode bitmasks S_IFDIR, S_IFREG and S_IFLNK. -** -** In some ways it would be better to obtain these values from system -** header files. But, the dependency is undesirable and (a) these -** have been stable for decades, (b) the values are part of POSIX and -** are also made explicit in [man stat], and (c) are part of the -** file format for zip archives. -*/ -#ifndef S_IFDIR -# define S_IFDIR 0040000 -#endif -#ifndef S_IFREG -# define S_IFREG 0100000 -#endif -#ifndef S_IFLNK -# define S_IFLNK 0120000 -#endif - -static const char ZIPFILE_SCHEMA[] = - "CREATE TABLE y(" - "name PRIMARY KEY," /* 0: Name of file in zip archive */ - "mode," /* 1: POSIX mode for file */ - "mtime," /* 2: Last modification time (secs since 1970)*/ - "sz," /* 3: Size of object */ - "rawdata," /* 4: Raw data */ - "data," /* 5: Uncompressed data */ - "method," /* 6: Compression method (integer) */ - "z HIDDEN" /* 7: Name of zip file */ - ") WITHOUT ROWID;"; - -#define ZIPFILE_F_COLUMN_IDX 7 /* Index of column "file" in the above */ -#define ZIPFILE_BUFFER_SIZE (64*1024) - - -/* -** Magic numbers used to read and write zip files. -** -** ZIPFILE_NEWENTRY_MADEBY: -** Use this value for the "version-made-by" field in new zip file -** entries. The upper byte indicates "unix", and the lower byte -** indicates that the zip file matches pkzip specification 3.0. -** This is what info-zip seems to do. -** -** ZIPFILE_NEWENTRY_REQUIRED: -** Value for "version-required-to-extract" field of new entries. -** Version 2.0 is required to support folders and deflate compression. -** -** ZIPFILE_NEWENTRY_FLAGS: -** Value for "general-purpose-bit-flags" field of new entries. Bit -** 11 means "utf-8 filename and comment". -** -** ZIPFILE_SIGNATURE_CDS: -** First 4 bytes of a valid CDS record. -** -** ZIPFILE_SIGNATURE_LFH: -** First 4 bytes of a valid LFH record. -** -** ZIPFILE_SIGNATURE_EOCD -** First 4 bytes of a valid EOCD record. -*/ -#define ZIPFILE_EXTRA_TIMESTAMP 0x5455 -#define ZIPFILE_NEWENTRY_MADEBY ((3<<8) + 30) -#define ZIPFILE_NEWENTRY_REQUIRED 20 -#define ZIPFILE_NEWENTRY_FLAGS 0x800 -#define ZIPFILE_SIGNATURE_CDS 0x02014b50 -#define ZIPFILE_SIGNATURE_LFH 0x04034b50 -#define ZIPFILE_SIGNATURE_EOCD 0x06054b50 - -/* -** The sizes of the fixed-size part of each of the three main data -** structures in a zip archive. -*/ -#define ZIPFILE_LFH_FIXED_SZ 30 -#define ZIPFILE_EOCD_FIXED_SZ 22 -#define ZIPFILE_CDS_FIXED_SZ 46 - -/* -*** 4.3.16 End of central directory record: -*** -*** end of central dir signature 4 bytes (0x06054b50) -*** number of this disk 2 bytes -*** number of the disk with the -*** start of the central directory 2 bytes -*** total number of entries in the -*** central directory on this disk 2 bytes -*** total number of entries in -*** the central directory 2 bytes -*** size of the central directory 4 bytes -*** offset of start of central -*** directory with respect to -*** the starting disk number 4 bytes -*** .ZIP file comment length 2 bytes -*** .ZIP file comment (variable size) -*/ -typedef struct ZipfileEOCD ZipfileEOCD; -struct ZipfileEOCD { - u16 iDisk; - u16 iFirstDisk; - u16 nEntry; - u16 nEntryTotal; - u32 nSize; - u32 iOffset; -}; - -/* -*** 4.3.12 Central directory structure: -*** -*** ... -*** -*** central file header signature 4 bytes (0x02014b50) -*** version made by 2 bytes -*** version needed to extract 2 bytes -*** general purpose bit flag 2 bytes -*** compression method 2 bytes -*** last mod file time 2 bytes -*** last mod file date 2 bytes -*** crc-32 4 bytes -*** compressed size 4 bytes -*** uncompressed size 4 bytes -*** file name length 2 bytes -*** extra field length 2 bytes -*** file comment length 2 bytes -*** disk number start 2 bytes -*** internal file attributes 2 bytes -*** external file attributes 4 bytes -*** relative offset of local header 4 bytes -*/ -typedef struct ZipfileCDS ZipfileCDS; -struct ZipfileCDS { - u16 iVersionMadeBy; - u16 iVersionExtract; - u16 flags; - u16 iCompression; - u16 mTime; - u16 mDate; - u32 crc32; - u32 szCompressed; - u32 szUncompressed; - u16 nFile; - u16 nExtra; - u16 nComment; - u16 iDiskStart; - u16 iInternalAttr; - u32 iExternalAttr; - u32 iOffset; - char *zFile; /* Filename (sqlite3_malloc()) */ -}; - -/* -*** 4.3.7 Local file header: -*** -*** local file header signature 4 bytes (0x04034b50) -*** version needed to extract 2 bytes -*** general purpose bit flag 2 bytes -*** compression method 2 bytes -*** last mod file time 2 bytes -*** last mod file date 2 bytes -*** crc-32 4 bytes -*** compressed size 4 bytes -*** uncompressed size 4 bytes -*** file name length 2 bytes -*** extra field length 2 bytes -*** -*/ -typedef struct ZipfileLFH ZipfileLFH; -struct ZipfileLFH { - u16 iVersionExtract; - u16 flags; - u16 iCompression; - u16 mTime; - u16 mDate; - u32 crc32; - u32 szCompressed; - u32 szUncompressed; - u16 nFile; - u16 nExtra; -}; - -typedef struct ZipfileEntry ZipfileEntry; -struct ZipfileEntry { - ZipfileCDS cds; /* Parsed CDS record */ - u32 mUnixTime; /* Modification time, in UNIX format */ - u8 *aExtra; /* cds.nExtra+cds.nComment bytes of extra data */ - i64 iDataOff; /* Offset to data in file (if aData==0) */ - u8 *aData; /* cds.szCompressed bytes of compressed data */ - ZipfileEntry *pNext; /* Next element in in-memory CDS */ -}; - -/* -** Cursor type for zipfile tables. -*/ -typedef struct ZipfileCsr ZipfileCsr; -struct ZipfileCsr { - sqlite3_vtab_cursor base; /* Base class - must be first */ - i64 iId; /* Cursor ID */ - u8 bEof; /* True when at EOF */ - u8 bNoop; /* If next xNext() call is no-op */ - - /* Used outside of write transactions */ - FILE *pFile; /* Zip file */ - i64 iNextOff; /* Offset of next record in central directory */ - ZipfileEOCD eocd; /* Parse of central directory record */ - - ZipfileEntry *pFreeEntry; /* Free this list when cursor is closed or reset */ - ZipfileEntry *pCurrent; /* Current entry */ - ZipfileCsr *pCsrNext; /* Next cursor on same virtual table */ -}; - -typedef struct ZipfileTab ZipfileTab; -struct ZipfileTab { - sqlite3_vtab base; /* Base class - must be first */ - char *zFile; /* Zip file this table accesses (may be NULL) */ - sqlite3 *db; /* Host database connection */ - u8 *aBuffer; /* Temporary buffer used for various tasks */ - - ZipfileCsr *pCsrList; /* List of cursors */ - i64 iNextCsrid; - - /* The following are used by write transactions only */ - ZipfileEntry *pFirstEntry; /* Linked list of all files (if pWriteFd!=0) */ - ZipfileEntry *pLastEntry; /* Last element in pFirstEntry list */ - FILE *pWriteFd; /* File handle open on zip archive */ - i64 szCurrent; /* Current size of zip archive */ - i64 szOrig; /* Size of archive at start of transaction */ -}; - -/* -** Set the error message contained in context ctx to the results of -** vprintf(zFmt, ...). -*/ -static void zipfileCtxErrorMsg(sqlite3_context *ctx, const char *zFmt, ...){ - char *zMsg = 0; - va_list ap; - va_start(ap, zFmt); - zMsg = sqlite3_vmprintf(zFmt, ap); - sqlite3_result_error(ctx, zMsg, -1); - sqlite3_free(zMsg); - va_end(ap); -} - -/* -** If string zIn is quoted, dequote it in place. Otherwise, if the string -** is not quoted, do nothing. -*/ -static void zipfileDequote(char *zIn){ - char q = zIn[0]; - if( q=='"' || q=='\'' || q=='`' || q=='[' ){ - int iIn = 1; - int iOut = 0; - if( q=='[' ) q = ']'; - while( ALWAYS(zIn[iIn]) ){ - char c = zIn[iIn++]; - if( c==q && zIn[iIn++]!=q ) break; - zIn[iOut++] = c; - } - zIn[iOut] = '\0'; - } -} - -/* -** Construct a new ZipfileTab virtual table object. -** -** argv[0] -> module name ("zipfile") -** argv[1] -> database name -** argv[2] -> table name -** argv[...] -> "column name" and other module argument fields. -*/ -static int zipfileConnect( - sqlite3 *db, - void *pAux, - int argc, const char *const*argv, - sqlite3_vtab **ppVtab, - char **pzErr -){ - int nByte = sizeof(ZipfileTab) + ZIPFILE_BUFFER_SIZE; - int nFile = 0; - const char *zFile = 0; - ZipfileTab *pNew = 0; - int rc; - - /* If the table name is not "zipfile", require that the argument be - ** specified. This stops zipfile tables from being created as: - ** - ** CREATE VIRTUAL TABLE zzz USING zipfile(); - ** - ** It does not prevent: - ** - ** CREATE VIRTUAL TABLE zipfile USING zipfile(); - */ - assert( 0==sqlite3_stricmp(argv[0], "zipfile") ); - if( (0!=sqlite3_stricmp(argv[2], "zipfile") && argc<4) || argc>4 ){ - *pzErr = sqlite3_mprintf("zipfile constructor requires one argument"); - return SQLITE_ERROR; - } - - if( argc>3 ){ - zFile = argv[3]; - nFile = (int)strlen(zFile)+1; - } - - rc = sqlite3_declare_vtab(db, ZIPFILE_SCHEMA); - if( rc==SQLITE_OK ){ - pNew = (ZipfileTab*)sqlite3_malloc64((sqlite3_int64)nByte+nFile); - if( pNew==0 ) return SQLITE_NOMEM; - memset(pNew, 0, nByte+nFile); - pNew->db = db; - pNew->aBuffer = (u8*)&pNew[1]; - if( zFile ){ - pNew->zFile = (char*)&pNew->aBuffer[ZIPFILE_BUFFER_SIZE]; - memcpy(pNew->zFile, zFile, nFile); - zipfileDequote(pNew->zFile); - } - } - sqlite3_vtab_config(db, SQLITE_VTAB_DIRECTONLY); - *ppVtab = (sqlite3_vtab*)pNew; - return rc; -} - -/* -** Free the ZipfileEntry structure indicated by the only argument. -*/ -static void zipfileEntryFree(ZipfileEntry *p){ - if( p ){ - sqlite3_free(p->cds.zFile); - sqlite3_free(p); - } -} - -/* -** Release resources that should be freed at the end of a write -** transaction. -*/ -static void zipfileCleanupTransaction(ZipfileTab *pTab){ - ZipfileEntry *pEntry; - ZipfileEntry *pNext; - - if( pTab->pWriteFd ){ - fclose(pTab->pWriteFd); - pTab->pWriteFd = 0; - } - for(pEntry=pTab->pFirstEntry; pEntry; pEntry=pNext){ - pNext = pEntry->pNext; - zipfileEntryFree(pEntry); - } - pTab->pFirstEntry = 0; - pTab->pLastEntry = 0; - pTab->szCurrent = 0; - pTab->szOrig = 0; -} - -/* -** This method is the destructor for zipfile vtab objects. -*/ -static int zipfileDisconnect(sqlite3_vtab *pVtab){ - zipfileCleanupTransaction((ZipfileTab*)pVtab); - sqlite3_free(pVtab); - return SQLITE_OK; -} - -/* -** Constructor for a new ZipfileCsr object. -*/ -static int zipfileOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCsr){ - ZipfileTab *pTab = (ZipfileTab*)p; - ZipfileCsr *pCsr; - pCsr = sqlite3_malloc(sizeof(*pCsr)); - *ppCsr = (sqlite3_vtab_cursor*)pCsr; - if( pCsr==0 ){ - return SQLITE_NOMEM; - } - memset(pCsr, 0, sizeof(*pCsr)); - pCsr->iId = ++pTab->iNextCsrid; - pCsr->pCsrNext = pTab->pCsrList; - pTab->pCsrList = pCsr; - return SQLITE_OK; -} - -/* -** Reset a cursor back to the state it was in when first returned -** by zipfileOpen(). -*/ -static void zipfileResetCursor(ZipfileCsr *pCsr){ - ZipfileEntry *p; - ZipfileEntry *pNext; - - pCsr->bEof = 0; - if( pCsr->pFile ){ - fclose(pCsr->pFile); - pCsr->pFile = 0; - zipfileEntryFree(pCsr->pCurrent); - pCsr->pCurrent = 0; - } - - for(p=pCsr->pFreeEntry; p; p=pNext){ - pNext = p->pNext; - zipfileEntryFree(p); - } -} - -/* -** Destructor for an ZipfileCsr. -*/ -static int zipfileClose(sqlite3_vtab_cursor *cur){ - ZipfileCsr *pCsr = (ZipfileCsr*)cur; - ZipfileTab *pTab = (ZipfileTab*)(pCsr->base.pVtab); - ZipfileCsr **pp; - zipfileResetCursor(pCsr); - - /* Remove this cursor from the ZipfileTab.pCsrList list. */ - for(pp=&pTab->pCsrList; *pp!=pCsr; pp=&((*pp)->pCsrNext)); - *pp = pCsr->pCsrNext; - - sqlite3_free(pCsr); - return SQLITE_OK; -} - -/* -** Set the error message for the virtual table associated with cursor -** pCsr to the results of vprintf(zFmt, ...). -*/ -static void zipfileTableErr(ZipfileTab *pTab, const char *zFmt, ...){ - va_list ap; - va_start(ap, zFmt); - sqlite3_free(pTab->base.zErrMsg); - pTab->base.zErrMsg = sqlite3_vmprintf(zFmt, ap); - va_end(ap); -} -static void zipfileCursorErr(ZipfileCsr *pCsr, const char *zFmt, ...){ - va_list ap; - va_start(ap, zFmt); - sqlite3_free(pCsr->base.pVtab->zErrMsg); - pCsr->base.pVtab->zErrMsg = sqlite3_vmprintf(zFmt, ap); - va_end(ap); -} - -/* -** Read nRead bytes of data from offset iOff of file pFile into buffer -** aRead[]. Return SQLITE_OK if successful, or an SQLite error code -** otherwise. -** -** If an error does occur, output variable (*pzErrmsg) may be set to point -** to an English language error message. It is the responsibility of the -** caller to eventually free this buffer using -** sqlite3_free(). -*/ -static int zipfileReadData( - FILE *pFile, /* Read from this file */ - u8 *aRead, /* Read into this buffer */ - int nRead, /* Number of bytes to read */ - i64 iOff, /* Offset to read from */ - char **pzErrmsg /* OUT: Error message (from sqlite3_malloc) */ -){ - size_t n; - fseek(pFile, (long)iOff, SEEK_SET); - n = fread(aRead, 1, nRead, pFile); - if( (int)n!=nRead ){ - *pzErrmsg = sqlite3_mprintf("error in fread()"); - return SQLITE_ERROR; - } - return SQLITE_OK; -} - -static int zipfileAppendData( - ZipfileTab *pTab, - const u8 *aWrite, - int nWrite -){ - size_t n; - fseek(pTab->pWriteFd, (long)pTab->szCurrent, SEEK_SET); - n = fwrite(aWrite, 1, nWrite, pTab->pWriteFd); - if( (int)n!=nWrite ){ - pTab->base.zErrMsg = sqlite3_mprintf("error in fwrite()"); - return SQLITE_ERROR; - } - pTab->szCurrent += nWrite; - return SQLITE_OK; -} - -/* -** Read and return a 16-bit little-endian unsigned integer from buffer aBuf. -*/ -static u16 zipfileGetU16(const u8 *aBuf){ - return (aBuf[1] << 8) + aBuf[0]; -} - -/* -** Read and return a 32-bit little-endian unsigned integer from buffer aBuf. -*/ -static u32 zipfileGetU32(const u8 *aBuf){ - return ((u32)(aBuf[3]) << 24) - + ((u32)(aBuf[2]) << 16) - + ((u32)(aBuf[1]) << 8) - + ((u32)(aBuf[0]) << 0); -} - -/* -** Write a 16-bit little endiate integer into buffer aBuf. -*/ -static void zipfilePutU16(u8 *aBuf, u16 val){ - aBuf[0] = val & 0xFF; - aBuf[1] = (val>>8) & 0xFF; -} - -/* -** Write a 32-bit little endiate integer into buffer aBuf. -*/ -static void zipfilePutU32(u8 *aBuf, u32 val){ - aBuf[0] = val & 0xFF; - aBuf[1] = (val>>8) & 0xFF; - aBuf[2] = (val>>16) & 0xFF; - aBuf[3] = (val>>24) & 0xFF; -} - -#define zipfileRead32(aBuf) ( aBuf+=4, zipfileGetU32(aBuf-4) ) -#define zipfileRead16(aBuf) ( aBuf+=2, zipfileGetU16(aBuf-2) ) - -#define zipfileWrite32(aBuf,val) { zipfilePutU32(aBuf,val); aBuf+=4; } -#define zipfileWrite16(aBuf,val) { zipfilePutU16(aBuf,val); aBuf+=2; } - -/* -** Magic numbers used to read CDS records. -*/ -#define ZIPFILE_CDS_NFILE_OFF 28 -#define ZIPFILE_CDS_SZCOMPRESSED_OFF 20 - -/* -** Decode the CDS record in buffer aBuf into (*pCDS). Return SQLITE_ERROR -** if the record is not well-formed, or SQLITE_OK otherwise. -*/ -static int zipfileReadCDS(u8 *aBuf, ZipfileCDS *pCDS){ - u8 *aRead = aBuf; - u32 sig = zipfileRead32(aRead); - int rc = SQLITE_OK; - if( sig!=ZIPFILE_SIGNATURE_CDS ){ - rc = SQLITE_ERROR; - }else{ - pCDS->iVersionMadeBy = zipfileRead16(aRead); - pCDS->iVersionExtract = zipfileRead16(aRead); - pCDS->flags = zipfileRead16(aRead); - pCDS->iCompression = zipfileRead16(aRead); - pCDS->mTime = zipfileRead16(aRead); - pCDS->mDate = zipfileRead16(aRead); - pCDS->crc32 = zipfileRead32(aRead); - pCDS->szCompressed = zipfileRead32(aRead); - pCDS->szUncompressed = zipfileRead32(aRead); - assert( aRead==&aBuf[ZIPFILE_CDS_NFILE_OFF] ); - pCDS->nFile = zipfileRead16(aRead); - pCDS->nExtra = zipfileRead16(aRead); - pCDS->nComment = zipfileRead16(aRead); - pCDS->iDiskStart = zipfileRead16(aRead); - pCDS->iInternalAttr = zipfileRead16(aRead); - pCDS->iExternalAttr = zipfileRead32(aRead); - pCDS->iOffset = zipfileRead32(aRead); - assert( aRead==&aBuf[ZIPFILE_CDS_FIXED_SZ] ); - } - - return rc; -} - -/* -** Decode the LFH record in buffer aBuf into (*pLFH). Return SQLITE_ERROR -** if the record is not well-formed, or SQLITE_OK otherwise. -*/ -static int zipfileReadLFH( - u8 *aBuffer, - ZipfileLFH *pLFH -){ - u8 *aRead = aBuffer; - int rc = SQLITE_OK; - - u32 sig = zipfileRead32(aRead); - if( sig!=ZIPFILE_SIGNATURE_LFH ){ - rc = SQLITE_ERROR; - }else{ - pLFH->iVersionExtract = zipfileRead16(aRead); - pLFH->flags = zipfileRead16(aRead); - pLFH->iCompression = zipfileRead16(aRead); - pLFH->mTime = zipfileRead16(aRead); - pLFH->mDate = zipfileRead16(aRead); - pLFH->crc32 = zipfileRead32(aRead); - pLFH->szCompressed = zipfileRead32(aRead); - pLFH->szUncompressed = zipfileRead32(aRead); - pLFH->nFile = zipfileRead16(aRead); - pLFH->nExtra = zipfileRead16(aRead); - } - return rc; -} - - -/* -** Buffer aExtra (size nExtra bytes) contains zip archive "extra" fields. -** Scan through this buffer to find an "extra-timestamp" field. If one -** exists, extract the 32-bit modification-timestamp from it and store -** the value in output parameter *pmTime. -** -** Zero is returned if no extra-timestamp record could be found (and so -** *pmTime is left unchanged), or non-zero otherwise. -** -** The general format of an extra field is: -** -** Header ID 2 bytes -** Data Size 2 bytes -** Data N bytes -*/ -static int zipfileScanExtra(u8 *aExtra, int nExtra, u32 *pmTime){ - int ret = 0; - u8 *p = aExtra; - u8 *pEnd = &aExtra[nExtra]; - - while( p modtime is present */ - *pmTime = zipfileGetU32(&p[1]); - ret = 1; - } - break; - } - } - - p += nByte; - } - return ret; -} - -/* -** Convert the standard MS-DOS timestamp stored in the mTime and mDate -** fields of the CDS structure passed as the only argument to a 32-bit -** UNIX seconds-since-the-epoch timestamp. Return the result. -** -** "Standard" MS-DOS time format: -** -** File modification time: -** Bits 00-04: seconds divided by 2 -** Bits 05-10: minute -** Bits 11-15: hour -** File modification date: -** Bits 00-04: day -** Bits 05-08: month (1-12) -** Bits 09-15: years from 1980 -** -** https://msdn.microsoft.com/en-us/library/9kkf9tah.aspx -*/ -static u32 zipfileMtime(ZipfileCDS *pCDS){ - int Y = (1980 + ((pCDS->mDate >> 9) & 0x7F)); - int M = ((pCDS->mDate >> 5) & 0x0F); - int D = (pCDS->mDate & 0x1F); - int B = -13; - - int sec = (pCDS->mTime & 0x1F)*2; - int min = (pCDS->mTime >> 5) & 0x3F; - int hr = (pCDS->mTime >> 11) & 0x1F; - i64 JD; - - /* JD = INT(365.25 * (Y+4716)) + INT(30.6001 * (M+1)) + D + B - 1524.5 */ - - /* Calculate the JD in seconds for noon on the day in question */ - if( M<3 ){ - Y = Y-1; - M = M+12; - } - JD = (i64)(24*60*60) * ( - (int)(365.25 * (Y + 4716)) - + (int)(30.6001 * (M + 1)) - + D + B - 1524 - ); - - /* Correct the JD for the time within the day */ - JD += (hr-12) * 3600 + min * 60 + sec; - - /* Convert JD to unix timestamp (the JD epoch is 2440587.5) */ - return (u32)(JD - (i64)(24405875) * 24*60*6); -} - -/* -** The opposite of zipfileMtime(). This function populates the mTime and -** mDate fields of the CDS structure passed as the first argument according -** to the UNIX timestamp value passed as the second. -*/ -static void zipfileMtimeToDos(ZipfileCDS *pCds, u32 mUnixTime){ - /* Convert unix timestamp to JD (2440588 is noon on 1/1/1970) */ - i64 JD = (i64)2440588 + mUnixTime / (24*60*60); - - int A, B, C, D, E; - int yr, mon, day; - int hr, min, sec; - - A = (int)((JD - 1867216.25)/36524.25); - A = (int)(JD + 1 + A - (A/4)); - B = A + 1524; - C = (int)((B - 122.1)/365.25); - D = (36525*(C&32767))/100; - E = (int)((B-D)/30.6001); - - day = B - D - (int)(30.6001*E); - mon = (E<14 ? E-1 : E-13); - yr = mon>2 ? C-4716 : C-4715; - - hr = (mUnixTime % (24*60*60)) / (60*60); - min = (mUnixTime % (60*60)) / 60; - sec = (mUnixTime % 60); - - if( yr>=1980 ){ - pCds->mDate = (u16)(day + (mon << 5) + ((yr-1980) << 9)); - pCds->mTime = (u16)(sec/2 + (min<<5) + (hr<<11)); - }else{ - pCds->mDate = pCds->mTime = 0; - } - - assert( mUnixTime<315507600 - || mUnixTime==zipfileMtime(pCds) - || ((mUnixTime % 2) && mUnixTime-1==zipfileMtime(pCds)) - /* || (mUnixTime % 2) */ - ); -} - -/* -** If aBlob is not NULL, then it is a pointer to a buffer (nBlob bytes in -** size) containing an entire zip archive image. Or, if aBlob is NULL, -** then pFile is a file-handle open on a zip file. In either case, this -** function creates a ZipfileEntry object based on the zip archive entry -** for which the CDS record is at offset iOff. -** -** If successful, SQLITE_OK is returned and (*ppEntry) set to point to -** the new object. Otherwise, an SQLite error code is returned and the -** final value of (*ppEntry) undefined. -*/ -static int zipfileGetEntry( - ZipfileTab *pTab, /* Store any error message here */ - const u8 *aBlob, /* Pointer to in-memory file image */ - int nBlob, /* Size of aBlob[] in bytes */ - FILE *pFile, /* If aBlob==0, read from this file */ - i64 iOff, /* Offset of CDS record */ - ZipfileEntry **ppEntry /* OUT: Pointer to new object */ -){ - u8 *aRead; - char **pzErr = &pTab->base.zErrMsg; - int rc = SQLITE_OK; - - if( aBlob==0 ){ - aRead = pTab->aBuffer; - rc = zipfileReadData(pFile, aRead, ZIPFILE_CDS_FIXED_SZ, iOff, pzErr); - }else{ - aRead = (u8*)&aBlob[iOff]; - } - - if( rc==SQLITE_OK ){ - sqlite3_int64 nAlloc; - ZipfileEntry *pNew; - - int nFile = zipfileGetU16(&aRead[ZIPFILE_CDS_NFILE_OFF]); - int nExtra = zipfileGetU16(&aRead[ZIPFILE_CDS_NFILE_OFF+2]); - nExtra += zipfileGetU16(&aRead[ZIPFILE_CDS_NFILE_OFF+4]); - - nAlloc = sizeof(ZipfileEntry) + nExtra; - if( aBlob ){ - nAlloc += zipfileGetU32(&aRead[ZIPFILE_CDS_SZCOMPRESSED_OFF]); - } - - pNew = (ZipfileEntry*)sqlite3_malloc64(nAlloc); - if( pNew==0 ){ - rc = SQLITE_NOMEM; - }else{ - memset(pNew, 0, sizeof(ZipfileEntry)); - rc = zipfileReadCDS(aRead, &pNew->cds); - if( rc!=SQLITE_OK ){ - *pzErr = sqlite3_mprintf("failed to read CDS at offset %lld", iOff); - }else if( aBlob==0 ){ - rc = zipfileReadData( - pFile, aRead, nExtra+nFile, iOff+ZIPFILE_CDS_FIXED_SZ, pzErr - ); - }else{ - aRead = (u8*)&aBlob[iOff + ZIPFILE_CDS_FIXED_SZ]; - } - } - - if( rc==SQLITE_OK ){ - u32 *pt = &pNew->mUnixTime; - pNew->cds.zFile = sqlite3_mprintf("%.*s", nFile, aRead); - pNew->aExtra = (u8*)&pNew[1]; - memcpy(pNew->aExtra, &aRead[nFile], nExtra); - if( pNew->cds.zFile==0 ){ - rc = SQLITE_NOMEM; - }else if( 0==zipfileScanExtra(&aRead[nFile], pNew->cds.nExtra, pt) ){ - pNew->mUnixTime = zipfileMtime(&pNew->cds); - } - } - - if( rc==SQLITE_OK ){ - static const int szFix = ZIPFILE_LFH_FIXED_SZ; - ZipfileLFH lfh; - if( pFile ){ - rc = zipfileReadData(pFile, aRead, szFix, pNew->cds.iOffset, pzErr); - }else{ - aRead = (u8*)&aBlob[pNew->cds.iOffset]; - } - - rc = zipfileReadLFH(aRead, &lfh); - if( rc==SQLITE_OK ){ - pNew->iDataOff = pNew->cds.iOffset + ZIPFILE_LFH_FIXED_SZ; - pNew->iDataOff += lfh.nFile + lfh.nExtra; - if( aBlob && pNew->cds.szCompressed ){ - pNew->aData = &pNew->aExtra[nExtra]; - memcpy(pNew->aData, &aBlob[pNew->iDataOff], pNew->cds.szCompressed); - } - }else{ - *pzErr = sqlite3_mprintf("failed to read LFH at offset %d", - (int)pNew->cds.iOffset - ); - } - } - - if( rc!=SQLITE_OK ){ - zipfileEntryFree(pNew); - }else{ - *ppEntry = pNew; - } - } - - return rc; -} - -/* -** Advance an ZipfileCsr to its next row of output. -*/ -static int zipfileNext(sqlite3_vtab_cursor *cur){ - ZipfileCsr *pCsr = (ZipfileCsr*)cur; - int rc = SQLITE_OK; - - if( pCsr->pFile ){ - i64 iEof = pCsr->eocd.iOffset + pCsr->eocd.nSize; - zipfileEntryFree(pCsr->pCurrent); - pCsr->pCurrent = 0; - if( pCsr->iNextOff>=iEof ){ - pCsr->bEof = 1; - }else{ - ZipfileEntry *p = 0; - ZipfileTab *pTab = (ZipfileTab*)(cur->pVtab); - rc = zipfileGetEntry(pTab, 0, 0, pCsr->pFile, pCsr->iNextOff, &p); - if( rc==SQLITE_OK ){ - pCsr->iNextOff += ZIPFILE_CDS_FIXED_SZ; - pCsr->iNextOff += (int)p->cds.nExtra + p->cds.nFile + p->cds.nComment; - } - pCsr->pCurrent = p; - } - }else{ - if( !pCsr->bNoop ){ - pCsr->pCurrent = pCsr->pCurrent->pNext; - } - if( pCsr->pCurrent==0 ){ - pCsr->bEof = 1; - } - } - - pCsr->bNoop = 0; - return rc; -} - -static void zipfileFree(void *p) { - sqlite3_free(p); -} - -/* -** Buffer aIn (size nIn bytes) contains compressed data. Uncompressed, the -** size is nOut bytes. This function uncompresses the data and sets the -** return value in context pCtx to the result (a blob). -** -** If an error occurs, an error code is left in pCtx instead. -*/ -static void zipfileInflate( - sqlite3_context *pCtx, /* Store result here */ - const u8 *aIn, /* Compressed data */ - int nIn, /* Size of buffer aIn[] in bytes */ - int nOut /* Expected output size */ -){ - u8 *aRes = sqlite3_malloc(nOut); - if( aRes==0 ){ - sqlite3_result_error_nomem(pCtx); - }else{ - int err; - z_stream str; - memset(&str, 0, sizeof(str)); - - str.next_in = (Byte*)aIn; - str.avail_in = nIn; - str.next_out = (Byte*)aRes; - str.avail_out = nOut; - - err = inflateInit2(&str, -15); - if( err!=Z_OK ){ - zipfileCtxErrorMsg(pCtx, "inflateInit2() failed (%d)", err); - }else{ - err = inflate(&str, Z_NO_FLUSH); - if( err!=Z_STREAM_END ){ - zipfileCtxErrorMsg(pCtx, "inflate() failed (%d)", err); - }else{ - sqlite3_result_blob(pCtx, aRes, nOut, zipfileFree); - aRes = 0; - } - } - sqlite3_free(aRes); - inflateEnd(&str); - } -} - -/* -** Buffer aIn (size nIn bytes) contains uncompressed data. This function -** compresses it and sets (*ppOut) to point to a buffer containing the -** compressed data. The caller is responsible for eventually calling -** sqlite3_free() to release buffer (*ppOut). Before returning, (*pnOut) -** is set to the size of buffer (*ppOut) in bytes. -** -** If no error occurs, SQLITE_OK is returned. Otherwise, an SQLite error -** code is returned and an error message left in virtual-table handle -** pTab. The values of (*ppOut) and (*pnOut) are left unchanged in this -** case. -*/ -static int zipfileDeflate( - const u8 *aIn, int nIn, /* Input */ - u8 **ppOut, int *pnOut, /* Output */ - char **pzErr /* OUT: Error message */ -){ - int rc = SQLITE_OK; - sqlite3_int64 nAlloc; - z_stream str; - u8 *aOut; - - memset(&str, 0, sizeof(str)); - str.next_in = (Bytef*)aIn; - str.avail_in = nIn; - deflateInit2(&str, 9, Z_DEFLATED, -15, 8, Z_DEFAULT_STRATEGY); - - nAlloc = deflateBound(&str, nIn); - aOut = (u8*)sqlite3_malloc64(nAlloc); - if( aOut==0 ){ - rc = SQLITE_NOMEM; - }else{ - int res; - str.next_out = aOut; - str.avail_out = nAlloc; - res = deflate(&str, Z_FINISH); - if( res==Z_STREAM_END ){ - *ppOut = aOut; - *pnOut = (int)str.total_out; - }else{ - sqlite3_free(aOut); - *pzErr = sqlite3_mprintf("zipfile: deflate() error"); - rc = SQLITE_ERROR; - } - deflateEnd(&str); - } - - return rc; -} - - -/* -** Return values of columns for the row at which the series_cursor -** is currently pointing. -*/ -static int zipfileColumn( - sqlite3_vtab_cursor *cur, /* The cursor */ - sqlite3_context *ctx, /* First argument to sqlite3_result_...() */ - int i /* Which column to return */ -){ - ZipfileCsr *pCsr = (ZipfileCsr*)cur; - ZipfileCDS *pCDS = &pCsr->pCurrent->cds; - int rc = SQLITE_OK; - switch( i ){ - case 0: /* name */ - sqlite3_result_text(ctx, pCDS->zFile, -1, SQLITE_TRANSIENT); - break; - case 1: /* mode */ - /* TODO: Whether or not the following is correct surely depends on - ** the platform on which the archive was created. */ - sqlite3_result_int(ctx, pCDS->iExternalAttr >> 16); - break; - case 2: { /* mtime */ - sqlite3_result_int64(ctx, pCsr->pCurrent->mUnixTime); - break; - } - case 3: { /* sz */ - if( sqlite3_vtab_nochange(ctx)==0 ){ - sqlite3_result_int64(ctx, pCDS->szUncompressed); - } - break; - } - case 4: /* rawdata */ - if( sqlite3_vtab_nochange(ctx) ) break; - case 5: { /* data */ - if( i==4 || pCDS->iCompression==0 || pCDS->iCompression==8 ){ - int sz = pCDS->szCompressed; - int szFinal = pCDS->szUncompressed; - if( szFinal>0 ){ - u8 *aBuf; - u8 *aFree = 0; - if( pCsr->pCurrent->aData ){ - aBuf = pCsr->pCurrent->aData; - }else{ - aBuf = aFree = sqlite3_malloc64(sz); - if( aBuf==0 ){ - rc = SQLITE_NOMEM; - }else{ - FILE *pFile = pCsr->pFile; - if( pFile==0 ){ - pFile = ((ZipfileTab*)(pCsr->base.pVtab))->pWriteFd; - } - rc = zipfileReadData(pFile, aBuf, sz, pCsr->pCurrent->iDataOff, - &pCsr->base.pVtab->zErrMsg - ); - } - } - if( rc==SQLITE_OK ){ - if( i==5 && pCDS->iCompression ){ - zipfileInflate(ctx, aBuf, sz, szFinal); - }else{ - sqlite3_result_blob(ctx, aBuf, sz, SQLITE_TRANSIENT); - } - } - sqlite3_free(aFree); - }else{ - /* Figure out if this is a directory or a zero-sized file. Consider - ** it to be a directory either if the mode suggests so, or if - ** the final character in the name is '/'. */ - u32 mode = pCDS->iExternalAttr >> 16; - if( !(mode & S_IFDIR) && pCDS->zFile[pCDS->nFile-1]!='/' ){ - sqlite3_result_blob(ctx, "", 0, SQLITE_STATIC); - } - } - } - break; - } - case 6: /* method */ - sqlite3_result_int(ctx, pCDS->iCompression); - break; - default: /* z */ - assert( i==7 ); - sqlite3_result_int64(ctx, pCsr->iId); - break; - } - - return rc; -} - -/* -** Return TRUE if the cursor is at EOF. -*/ -static int zipfileEof(sqlite3_vtab_cursor *cur){ - ZipfileCsr *pCsr = (ZipfileCsr*)cur; - return pCsr->bEof; -} - -/* -** If aBlob is not NULL, then it points to a buffer nBlob bytes in size -** containing an entire zip archive image. Or, if aBlob is NULL, then pFile -** is guaranteed to be a file-handle open on a zip file. -** -** This function attempts to locate the EOCD record within the zip archive -** and populate *pEOCD with the results of decoding it. SQLITE_OK is -** returned if successful. Otherwise, an SQLite error code is returned and -** an English language error message may be left in virtual-table pTab. -*/ -static int zipfileReadEOCD( - ZipfileTab *pTab, /* Return errors here */ - const u8 *aBlob, /* Pointer to in-memory file image */ - int nBlob, /* Size of aBlob[] in bytes */ - FILE *pFile, /* Read from this file if aBlob==0 */ - ZipfileEOCD *pEOCD /* Object to populate */ -){ - u8 *aRead = pTab->aBuffer; /* Temporary buffer */ - int nRead; /* Bytes to read from file */ - int rc = SQLITE_OK; - - if( aBlob==0 ){ - i64 iOff; /* Offset to read from */ - i64 szFile; /* Total size of file in bytes */ - fseek(pFile, 0, SEEK_END); - szFile = (i64)ftell(pFile); - if( szFile==0 ){ - memset(pEOCD, 0, sizeof(ZipfileEOCD)); - return SQLITE_OK; - } - nRead = (int)(MIN(szFile, ZIPFILE_BUFFER_SIZE)); - iOff = szFile - nRead; - rc = zipfileReadData(pFile, aRead, nRead, iOff, &pTab->base.zErrMsg); - }else{ - nRead = (int)(MIN(nBlob, ZIPFILE_BUFFER_SIZE)); - aRead = (u8*)&aBlob[nBlob-nRead]; - } - - if( rc==SQLITE_OK ){ - int i; - - /* Scan backwards looking for the signature bytes */ - for(i=nRead-20; i>=0; i--){ - if( aRead[i]==0x50 && aRead[i+1]==0x4b - && aRead[i+2]==0x05 && aRead[i+3]==0x06 - ){ - break; - } - } - if( i<0 ){ - pTab->base.zErrMsg = sqlite3_mprintf( - "cannot find end of central directory record" - ); - return SQLITE_ERROR; - } - - aRead += i+4; - pEOCD->iDisk = zipfileRead16(aRead); - pEOCD->iFirstDisk = zipfileRead16(aRead); - pEOCD->nEntry = zipfileRead16(aRead); - pEOCD->nEntryTotal = zipfileRead16(aRead); - pEOCD->nSize = zipfileRead32(aRead); - pEOCD->iOffset = zipfileRead32(aRead); - } - - return rc; -} - -/* -** Add object pNew to the linked list that begins at ZipfileTab.pFirstEntry -** and ends with pLastEntry. If argument pBefore is NULL, then pNew is added -** to the end of the list. Otherwise, it is added to the list immediately -** before pBefore (which is guaranteed to be a part of said list). -*/ -static void zipfileAddEntry( - ZipfileTab *pTab, - ZipfileEntry *pBefore, - ZipfileEntry *pNew -){ - assert( (pTab->pFirstEntry==0)==(pTab->pLastEntry==0) ); - assert( pNew->pNext==0 ); - if( pBefore==0 ){ - if( pTab->pFirstEntry==0 ){ - pTab->pFirstEntry = pTab->pLastEntry = pNew; - }else{ - assert( pTab->pLastEntry->pNext==0 ); - pTab->pLastEntry->pNext = pNew; - pTab->pLastEntry = pNew; - } - }else{ - ZipfileEntry **pp; - for(pp=&pTab->pFirstEntry; *pp!=pBefore; pp=&((*pp)->pNext)); - pNew->pNext = pBefore; - *pp = pNew; - } -} - -static int zipfileLoadDirectory(ZipfileTab *pTab, const u8 *aBlob, int nBlob){ - ZipfileEOCD eocd; - int rc; - int i; - i64 iOff; - - rc = zipfileReadEOCD(pTab, aBlob, nBlob, pTab->pWriteFd, &eocd); - iOff = eocd.iOffset; - for(i=0; rc==SQLITE_OK && ipWriteFd, iOff, &pNew); - - if( rc==SQLITE_OK ){ - zipfileAddEntry(pTab, 0, pNew); - iOff += ZIPFILE_CDS_FIXED_SZ; - iOff += (int)pNew->cds.nExtra + pNew->cds.nFile + pNew->cds.nComment; - } - } - return rc; -} - -/* -** xFilter callback. -*/ -static int zipfileFilter( - sqlite3_vtab_cursor *cur, - int idxNum, const char *idxStr, - int argc, sqlite3_value **argv -){ - ZipfileTab *pTab = (ZipfileTab*)cur->pVtab; - ZipfileCsr *pCsr = (ZipfileCsr*)cur; - const char *zFile = 0; /* Zip file to scan */ - int rc = SQLITE_OK; /* Return Code */ - int bInMemory = 0; /* True for an in-memory zipfile */ - - zipfileResetCursor(pCsr); - - if( pTab->zFile ){ - zFile = pTab->zFile; - }else if( idxNum==0 ){ - zipfileCursorErr(pCsr, "zipfile() function requires an argument"); - return SQLITE_ERROR; - }else if( sqlite3_value_type(argv[0])==SQLITE_BLOB ){ - const u8 *aBlob = (const u8*)sqlite3_value_blob(argv[0]); - int nBlob = sqlite3_value_bytes(argv[0]); - assert( pTab->pFirstEntry==0 ); - rc = zipfileLoadDirectory(pTab, aBlob, nBlob); - pCsr->pFreeEntry = pTab->pFirstEntry; - pTab->pFirstEntry = pTab->pLastEntry = 0; - if( rc!=SQLITE_OK ) return rc; - bInMemory = 1; - }else{ - zFile = (const char*)sqlite3_value_text(argv[0]); - } - - if( 0==pTab->pWriteFd && 0==bInMemory ){ - pCsr->pFile = fopen(zFile, "rb"); - if( pCsr->pFile==0 ){ - zipfileCursorErr(pCsr, "cannot open file: %s", zFile); - rc = SQLITE_ERROR; - }else{ - rc = zipfileReadEOCD(pTab, 0, 0, pCsr->pFile, &pCsr->eocd); - if( rc==SQLITE_OK ){ - if( pCsr->eocd.nEntry==0 ){ - pCsr->bEof = 1; - }else{ - pCsr->iNextOff = pCsr->eocd.iOffset; - rc = zipfileNext(cur); - } - } - } - }else{ - pCsr->bNoop = 1; - pCsr->pCurrent = pCsr->pFreeEntry ? pCsr->pFreeEntry : pTab->pFirstEntry; - rc = zipfileNext(cur); - } - - return rc; -} - -/* -** xBestIndex callback. -*/ -static int zipfileBestIndex( - sqlite3_vtab *tab, - sqlite3_index_info *pIdxInfo -){ - int i; - int idx = -1; - int unusable = 0; - - for(i=0; inConstraint; i++){ - const struct sqlite3_index_constraint *pCons = &pIdxInfo->aConstraint[i]; - if( pCons->iColumn!=ZIPFILE_F_COLUMN_IDX ) continue; - if( pCons->usable==0 ){ - unusable = 1; - }else if( pCons->op==SQLITE_INDEX_CONSTRAINT_EQ ){ - idx = i; - } - } - pIdxInfo->estimatedCost = 1000.0; - if( idx>=0 ){ - pIdxInfo->aConstraintUsage[idx].argvIndex = 1; - pIdxInfo->aConstraintUsage[idx].omit = 1; - pIdxInfo->idxNum = 1; - }else if( unusable ){ - return SQLITE_CONSTRAINT; - } - return SQLITE_OK; -} - -static ZipfileEntry *zipfileNewEntry(const char *zPath){ - ZipfileEntry *pNew; - pNew = sqlite3_malloc(sizeof(ZipfileEntry)); - if( pNew ){ - memset(pNew, 0, sizeof(ZipfileEntry)); - pNew->cds.zFile = sqlite3_mprintf("%s", zPath); - if( pNew->cds.zFile==0 ){ - sqlite3_free(pNew); - pNew = 0; - } - } - return pNew; -} - -static int zipfileSerializeLFH(ZipfileEntry *pEntry, u8 *aBuf){ - ZipfileCDS *pCds = &pEntry->cds; - u8 *a = aBuf; - - pCds->nExtra = 9; - - /* Write the LFH itself */ - zipfileWrite32(a, ZIPFILE_SIGNATURE_LFH); - zipfileWrite16(a, pCds->iVersionExtract); - zipfileWrite16(a, pCds->flags); - zipfileWrite16(a, pCds->iCompression); - zipfileWrite16(a, pCds->mTime); - zipfileWrite16(a, pCds->mDate); - zipfileWrite32(a, pCds->crc32); - zipfileWrite32(a, pCds->szCompressed); - zipfileWrite32(a, pCds->szUncompressed); - zipfileWrite16(a, (u16)pCds->nFile); - zipfileWrite16(a, pCds->nExtra); - assert( a==&aBuf[ZIPFILE_LFH_FIXED_SZ] ); - - /* Add the file name */ - memcpy(a, pCds->zFile, (int)pCds->nFile); - a += (int)pCds->nFile; - - /* The "extra" data */ - zipfileWrite16(a, ZIPFILE_EXTRA_TIMESTAMP); - zipfileWrite16(a, 5); - *a++ = 0x01; - zipfileWrite32(a, pEntry->mUnixTime); - - return a-aBuf; -} - -static int zipfileAppendEntry( - ZipfileTab *pTab, - ZipfileEntry *pEntry, - const u8 *pData, - int nData -){ - u8 *aBuf = pTab->aBuffer; - int nBuf; - int rc; - - nBuf = zipfileSerializeLFH(pEntry, aBuf); - rc = zipfileAppendData(pTab, aBuf, nBuf); - if( rc==SQLITE_OK ){ - pEntry->iDataOff = pTab->szCurrent; - rc = zipfileAppendData(pTab, pData, nData); - } - - return rc; -} - -static int zipfileGetMode( - sqlite3_value *pVal, - int bIsDir, /* If true, default to directory */ - u32 *pMode, /* OUT: Mode value */ - char **pzErr /* OUT: Error message */ -){ - const char *z = (const char*)sqlite3_value_text(pVal); - u32 mode = 0; - if( z==0 ){ - mode = (bIsDir ? (S_IFDIR + 0755) : (S_IFREG + 0644)); - }else if( z[0]>='0' && z[0]<='9' ){ - mode = (unsigned int)sqlite3_value_int(pVal); - }else{ - const char zTemplate[11] = "-rwxrwxrwx"; - int i; - if( strlen(z)!=10 ) goto parse_error; - switch( z[0] ){ - case '-': mode |= S_IFREG; break; - case 'd': mode |= S_IFDIR; break; - case 'l': mode |= S_IFLNK; break; - default: goto parse_error; - } - for(i=1; i<10; i++){ - if( z[i]==zTemplate[i] ) mode |= 1 << (9-i); - else if( z[i]!='-' ) goto parse_error; - } - } - if( ((mode & S_IFDIR)==0)==bIsDir ){ - /* The "mode" attribute is a directory, but data has been specified. - ** Or vice-versa - no data but "mode" is a file or symlink. */ - *pzErr = sqlite3_mprintf("zipfile: mode does not match data"); - return SQLITE_CONSTRAINT; - } - *pMode = mode; - return SQLITE_OK; - - parse_error: - *pzErr = sqlite3_mprintf("zipfile: parse error in mode: %s", z); - return SQLITE_ERROR; -} - -/* -** Both (const char*) arguments point to nul-terminated strings. Argument -** nB is the value of strlen(zB). This function returns 0 if the strings are -** identical, ignoring any trailing '/' character in either path. */ -static int zipfileComparePath(const char *zA, const char *zB, int nB){ - int nA = (int)strlen(zA); - if( nA>0 && zA[nA-1]=='/' ) nA--; - if( nB>0 && zB[nB-1]=='/' ) nB--; - if( nA==nB && memcmp(zA, zB, nA)==0 ) return 0; - return 1; -} - -static int zipfileBegin(sqlite3_vtab *pVtab){ - ZipfileTab *pTab = (ZipfileTab*)pVtab; - int rc = SQLITE_OK; - - assert( pTab->pWriteFd==0 ); - if( pTab->zFile==0 || pTab->zFile[0]==0 ){ - pTab->base.zErrMsg = sqlite3_mprintf("zipfile: missing filename"); - return SQLITE_ERROR; - } - - /* Open a write fd on the file. Also load the entire central directory - ** structure into memory. During the transaction any new file data is - ** appended to the archive file, but the central directory is accumulated - ** in main-memory until the transaction is committed. */ - pTab->pWriteFd = fopen(pTab->zFile, "ab+"); - if( pTab->pWriteFd==0 ){ - pTab->base.zErrMsg = sqlite3_mprintf( - "zipfile: failed to open file %s for writing", pTab->zFile - ); - rc = SQLITE_ERROR; - }else{ - fseek(pTab->pWriteFd, 0, SEEK_END); - pTab->szCurrent = pTab->szOrig = (i64)ftell(pTab->pWriteFd); - rc = zipfileLoadDirectory(pTab, 0, 0); - } - - if( rc!=SQLITE_OK ){ - zipfileCleanupTransaction(pTab); - } - - return rc; -} - -/* -** Return the current time as a 32-bit timestamp in UNIX epoch format (like -** time(2)). -*/ -static u32 zipfileTime(void){ - sqlite3_vfs *pVfs = sqlite3_vfs_find(0); - u32 ret; - if( pVfs->iVersion>=2 && pVfs->xCurrentTimeInt64 ){ - i64 ms; - pVfs->xCurrentTimeInt64(pVfs, &ms); - ret = (u32)((ms/1000) - ((i64)24405875 * 8640)); - }else{ - double day; - pVfs->xCurrentTime(pVfs, &day); - ret = (u32)((day - 2440587.5) * 86400); - } - return ret; -} - -/* -** Return a 32-bit timestamp in UNIX epoch format. -** -** If the value passed as the only argument is either NULL or an SQL NULL, -** return the current time. Otherwise, return the value stored in (*pVal) -** cast to a 32-bit unsigned integer. -*/ -static u32 zipfileGetTime(sqlite3_value *pVal){ - if( pVal==0 || sqlite3_value_type(pVal)==SQLITE_NULL ){ - return zipfileTime(); - } - return (u32)sqlite3_value_int64(pVal); -} - -/* -** Unless it is NULL, entry pOld is currently part of the pTab->pFirstEntry -** linked list. Remove it from the list and free the object. -*/ -static void zipfileRemoveEntryFromList(ZipfileTab *pTab, ZipfileEntry *pOld){ - if( pOld ){ - ZipfileEntry **pp; - for(pp=&pTab->pFirstEntry; (*pp)!=pOld; pp=&((*pp)->pNext)); - *pp = (*pp)->pNext; - zipfileEntryFree(pOld); - } -} - -/* -** xUpdate method. -*/ -static int zipfileUpdate( - sqlite3_vtab *pVtab, - int nVal, - sqlite3_value **apVal, - sqlite_int64 *pRowid -){ - ZipfileTab *pTab = (ZipfileTab*)pVtab; - int rc = SQLITE_OK; /* Return Code */ - ZipfileEntry *pNew = 0; /* New in-memory CDS entry */ - - u32 mode = 0; /* Mode for new entry */ - u32 mTime = 0; /* Modification time for new entry */ - i64 sz = 0; /* Uncompressed size */ - const char *zPath = 0; /* Path for new entry */ - int nPath = 0; /* strlen(zPath) */ - const u8 *pData = 0; /* Pointer to buffer containing content */ - int nData = 0; /* Size of pData buffer in bytes */ - int iMethod = 0; /* Compression method for new entry */ - u8 *pFree = 0; /* Free this */ - char *zFree = 0; /* Also free this */ - ZipfileEntry *pOld = 0; - ZipfileEntry *pOld2 = 0; - int bUpdate = 0; /* True for an update that modifies "name" */ - int bIsDir = 0; - u32 iCrc32 = 0; - - if( pTab->pWriteFd==0 ){ - rc = zipfileBegin(pVtab); - if( rc!=SQLITE_OK ) return rc; - } - - /* If this is a DELETE or UPDATE, find the archive entry to delete. */ - if( sqlite3_value_type(apVal[0])!=SQLITE_NULL ){ - const char *zDelete = (const char*)sqlite3_value_text(apVal[0]); - int nDelete = (int)strlen(zDelete); - if( nVal>1 ){ - const char *zUpdate = (const char*)sqlite3_value_text(apVal[1]); - if( zUpdate && zipfileComparePath(zUpdate, zDelete, nDelete)!=0 ){ - bUpdate = 1; - } - } - for(pOld=pTab->pFirstEntry; 1; pOld=pOld->pNext){ - if( zipfileComparePath(pOld->cds.zFile, zDelete, nDelete)==0 ){ - break; - } - assert( pOld->pNext ); - } - } - - if( nVal>1 ){ - /* Check that "sz" and "rawdata" are both NULL: */ - if( sqlite3_value_type(apVal[5])!=SQLITE_NULL ){ - zipfileTableErr(pTab, "sz must be NULL"); - rc = SQLITE_CONSTRAINT; - } - if( sqlite3_value_type(apVal[6])!=SQLITE_NULL ){ - zipfileTableErr(pTab, "rawdata must be NULL"); - rc = SQLITE_CONSTRAINT; - } - - if( rc==SQLITE_OK ){ - if( sqlite3_value_type(apVal[7])==SQLITE_NULL ){ - /* data=NULL. A directory */ - bIsDir = 1; - }else{ - /* Value specified for "data", and possibly "method". This must be - ** a regular file or a symlink. */ - const u8 *aIn = sqlite3_value_blob(apVal[7]); - int nIn = sqlite3_value_bytes(apVal[7]); - int bAuto = sqlite3_value_type(apVal[8])==SQLITE_NULL; - - iMethod = sqlite3_value_int(apVal[8]); - sz = nIn; - pData = aIn; - nData = nIn; - if( iMethod!=0 && iMethod!=8 ){ - zipfileTableErr(pTab, "unknown compression method: %d", iMethod); - rc = SQLITE_CONSTRAINT; - }else{ - if( bAuto || iMethod ){ - int nCmp; - rc = zipfileDeflate(aIn, nIn, &pFree, &nCmp, &pTab->base.zErrMsg); - if( rc==SQLITE_OK ){ - if( iMethod || nCmpbase.zErrMsg); - } - - if( rc==SQLITE_OK ){ - zPath = (const char*)sqlite3_value_text(apVal[2]); - if( zPath==0 ) zPath = ""; - nPath = (int)strlen(zPath); - mTime = zipfileGetTime(apVal[4]); - } - - if( rc==SQLITE_OK && bIsDir ){ - /* For a directory, check that the last character in the path is a - ** '/'. This appears to be required for compatibility with info-zip - ** (the unzip command on unix). It does not create directories - ** otherwise. */ - if( nPath<=0 || zPath[nPath-1]!='/' ){ - zFree = sqlite3_mprintf("%s/", zPath); - zPath = (const char*)zFree; - if( zFree==0 ){ - rc = SQLITE_NOMEM; - nPath = 0; - }else{ - nPath = (int)strlen(zPath); - } - } - } - - /* Check that we're not inserting a duplicate entry -OR- updating an - ** entry with a path, thereby making it into a duplicate. */ - if( (pOld==0 || bUpdate) && rc==SQLITE_OK ){ - ZipfileEntry *p; - for(p=pTab->pFirstEntry; p; p=p->pNext){ - if( zipfileComparePath(p->cds.zFile, zPath, nPath)==0 ){ - switch( sqlite3_vtab_on_conflict(pTab->db) ){ - case SQLITE_IGNORE: { - goto zipfile_update_done; - } - case SQLITE_REPLACE: { - pOld2 = p; - break; - } - default: { - zipfileTableErr(pTab, "duplicate name: \"%s\"", zPath); - rc = SQLITE_CONSTRAINT; - break; - } - } - break; - } - } - } - - if( rc==SQLITE_OK ){ - /* Create the new CDS record. */ - pNew = zipfileNewEntry(zPath); - if( pNew==0 ){ - rc = SQLITE_NOMEM; - }else{ - pNew->cds.iVersionMadeBy = ZIPFILE_NEWENTRY_MADEBY; - pNew->cds.iVersionExtract = ZIPFILE_NEWENTRY_REQUIRED; - pNew->cds.flags = ZIPFILE_NEWENTRY_FLAGS; - pNew->cds.iCompression = (u16)iMethod; - zipfileMtimeToDos(&pNew->cds, mTime); - pNew->cds.crc32 = iCrc32; - pNew->cds.szCompressed = nData; - pNew->cds.szUncompressed = (u32)sz; - pNew->cds.iExternalAttr = (mode<<16); - pNew->cds.iOffset = (u32)pTab->szCurrent; - pNew->cds.nFile = (u16)nPath; - pNew->mUnixTime = (u32)mTime; - rc = zipfileAppendEntry(pTab, pNew, pData, nData); - zipfileAddEntry(pTab, pOld, pNew); - } - } - } - - if( rc==SQLITE_OK && (pOld || pOld2) ){ - ZipfileCsr *pCsr; - for(pCsr=pTab->pCsrList; pCsr; pCsr=pCsr->pCsrNext){ - if( pCsr->pCurrent && (pCsr->pCurrent==pOld || pCsr->pCurrent==pOld2) ){ - pCsr->pCurrent = pCsr->pCurrent->pNext; - pCsr->bNoop = 1; - } - } - - zipfileRemoveEntryFromList(pTab, pOld); - zipfileRemoveEntryFromList(pTab, pOld2); - } - -zipfile_update_done: - sqlite3_free(pFree); - sqlite3_free(zFree); - return rc; -} - -static int zipfileSerializeEOCD(ZipfileEOCD *p, u8 *aBuf){ - u8 *a = aBuf; - zipfileWrite32(a, ZIPFILE_SIGNATURE_EOCD); - zipfileWrite16(a, p->iDisk); - zipfileWrite16(a, p->iFirstDisk); - zipfileWrite16(a, p->nEntry); - zipfileWrite16(a, p->nEntryTotal); - zipfileWrite32(a, p->nSize); - zipfileWrite32(a, p->iOffset); - zipfileWrite16(a, 0); /* Size of trailing comment in bytes*/ - - return a-aBuf; -} - -static int zipfileAppendEOCD(ZipfileTab *pTab, ZipfileEOCD *p){ - int nBuf = zipfileSerializeEOCD(p, pTab->aBuffer); - assert( nBuf==ZIPFILE_EOCD_FIXED_SZ ); - return zipfileAppendData(pTab, pTab->aBuffer, nBuf); -} - -/* -** Serialize the CDS structure into buffer aBuf[]. Return the number -** of bytes written. -*/ -static int zipfileSerializeCDS(ZipfileEntry *pEntry, u8 *aBuf){ - u8 *a = aBuf; - ZipfileCDS *pCDS = &pEntry->cds; - - if( pEntry->aExtra==0 ){ - pCDS->nExtra = 9; - } - - zipfileWrite32(a, ZIPFILE_SIGNATURE_CDS); - zipfileWrite16(a, pCDS->iVersionMadeBy); - zipfileWrite16(a, pCDS->iVersionExtract); - zipfileWrite16(a, pCDS->flags); - zipfileWrite16(a, pCDS->iCompression); - zipfileWrite16(a, pCDS->mTime); - zipfileWrite16(a, pCDS->mDate); - zipfileWrite32(a, pCDS->crc32); - zipfileWrite32(a, pCDS->szCompressed); - zipfileWrite32(a, pCDS->szUncompressed); - assert( a==&aBuf[ZIPFILE_CDS_NFILE_OFF] ); - zipfileWrite16(a, pCDS->nFile); - zipfileWrite16(a, pCDS->nExtra); - zipfileWrite16(a, pCDS->nComment); - zipfileWrite16(a, pCDS->iDiskStart); - zipfileWrite16(a, pCDS->iInternalAttr); - zipfileWrite32(a, pCDS->iExternalAttr); - zipfileWrite32(a, pCDS->iOffset); - - memcpy(a, pCDS->zFile, pCDS->nFile); - a += pCDS->nFile; - - if( pEntry->aExtra ){ - int n = (int)pCDS->nExtra + (int)pCDS->nComment; - memcpy(a, pEntry->aExtra, n); - a += n; - }else{ - assert( pCDS->nExtra==9 ); - zipfileWrite16(a, ZIPFILE_EXTRA_TIMESTAMP); - zipfileWrite16(a, 5); - *a++ = 0x01; - zipfileWrite32(a, pEntry->mUnixTime); - } - - return a-aBuf; -} - -static int zipfileCommit(sqlite3_vtab *pVtab){ - ZipfileTab *pTab = (ZipfileTab*)pVtab; - int rc = SQLITE_OK; - if( pTab->pWriteFd ){ - i64 iOffset = pTab->szCurrent; - ZipfileEntry *p; - ZipfileEOCD eocd; - int nEntry = 0; - - /* Write out all entries */ - for(p=pTab->pFirstEntry; rc==SQLITE_OK && p; p=p->pNext){ - int n = zipfileSerializeCDS(p, pTab->aBuffer); - rc = zipfileAppendData(pTab, pTab->aBuffer, n); - nEntry++; - } - - /* Write out the EOCD record */ - eocd.iDisk = 0; - eocd.iFirstDisk = 0; - eocd.nEntry = (u16)nEntry; - eocd.nEntryTotal = (u16)nEntry; - eocd.nSize = (u32)(pTab->szCurrent - iOffset); - eocd.iOffset = (u32)iOffset; - rc = zipfileAppendEOCD(pTab, &eocd); - - zipfileCleanupTransaction(pTab); - } - return rc; -} - -static int zipfileRollback(sqlite3_vtab *pVtab){ - return zipfileCommit(pVtab); -} - -static ZipfileCsr *zipfileFindCursor(ZipfileTab *pTab, i64 iId){ - ZipfileCsr *pCsr; - for(pCsr=pTab->pCsrList; pCsr; pCsr=pCsr->pCsrNext){ - if( iId==pCsr->iId ) break; - } - return pCsr; -} - -static void zipfileFunctionCds( - sqlite3_context *context, - int argc, - sqlite3_value **argv -){ - ZipfileCsr *pCsr; - ZipfileTab *pTab = (ZipfileTab*)sqlite3_user_data(context); - assert( argc>0 ); - - pCsr = zipfileFindCursor(pTab, sqlite3_value_int64(argv[0])); - if( pCsr ){ - ZipfileCDS *p = &pCsr->pCurrent->cds; - char *zRes = sqlite3_mprintf("{" - "\"version-made-by\" : %u, " - "\"version-to-extract\" : %u, " - "\"flags\" : %u, " - "\"compression\" : %u, " - "\"time\" : %u, " - "\"date\" : %u, " - "\"crc32\" : %u, " - "\"compressed-size\" : %u, " - "\"uncompressed-size\" : %u, " - "\"file-name-length\" : %u, " - "\"extra-field-length\" : %u, " - "\"file-comment-length\" : %u, " - "\"disk-number-start\" : %u, " - "\"internal-attr\" : %u, " - "\"external-attr\" : %u, " - "\"offset\" : %u }", - (u32)p->iVersionMadeBy, (u32)p->iVersionExtract, - (u32)p->flags, (u32)p->iCompression, - (u32)p->mTime, (u32)p->mDate, - (u32)p->crc32, (u32)p->szCompressed, - (u32)p->szUncompressed, (u32)p->nFile, - (u32)p->nExtra, (u32)p->nComment, - (u32)p->iDiskStart, (u32)p->iInternalAttr, - (u32)p->iExternalAttr, (u32)p->iOffset - ); - - if( zRes==0 ){ - sqlite3_result_error_nomem(context); - }else{ - sqlite3_result_text(context, zRes, -1, SQLITE_TRANSIENT); - sqlite3_free(zRes); - } - } -} - -/* -** xFindFunction method. -*/ -static int zipfileFindFunction( - sqlite3_vtab *pVtab, /* Virtual table handle */ - int nArg, /* Number of SQL function arguments */ - const char *zName, /* Name of SQL function */ - void (**pxFunc)(sqlite3_context*,int,sqlite3_value**), /* OUT: Result */ - void **ppArg /* OUT: User data for *pxFunc */ -){ - if( sqlite3_stricmp("zipfile_cds", zName)==0 ){ - *pxFunc = zipfileFunctionCds; - *ppArg = (void*)pVtab; - return 1; - } - return 0; -} - -typedef struct ZipfileBuffer ZipfileBuffer; -struct ZipfileBuffer { - u8 *a; /* Pointer to buffer */ - int n; /* Size of buffer in bytes */ - int nAlloc; /* Byte allocated at a[] */ -}; - -typedef struct ZipfileCtx ZipfileCtx; -struct ZipfileCtx { - int nEntry; - ZipfileBuffer body; - ZipfileBuffer cds; -}; - -static int zipfileBufferGrow(ZipfileBuffer *pBuf, int nByte){ - if( pBuf->n+nByte>pBuf->nAlloc ){ - u8 *aNew; - sqlite3_int64 nNew = pBuf->n ? pBuf->n*2 : 512; - int nReq = pBuf->n + nByte; - - while( nNewa, nNew); - if( aNew==0 ) return SQLITE_NOMEM; - pBuf->a = aNew; - pBuf->nAlloc = (int)nNew; - } - return SQLITE_OK; -} - -/* -** xStep() callback for the zipfile() aggregate. This can be called in -** any of the following ways: -** -** SELECT zipfile(name,data) ... -** SELECT zipfile(name,mode,mtime,data) ... -** SELECT zipfile(name,mode,mtime,data,method) ... -*/ -void zipfileStep(sqlite3_context *pCtx, int nVal, sqlite3_value **apVal){ - ZipfileCtx *p; /* Aggregate function context */ - ZipfileEntry e; /* New entry to add to zip archive */ - - sqlite3_value *pName = 0; - sqlite3_value *pMode = 0; - sqlite3_value *pMtime = 0; - sqlite3_value *pData = 0; - sqlite3_value *pMethod = 0; - - int bIsDir = 0; - u32 mode; - int rc = SQLITE_OK; - char *zErr = 0; - - int iMethod = -1; /* Compression method to use (0 or 8) */ - - const u8 *aData = 0; /* Possibly compressed data for new entry */ - int nData = 0; /* Size of aData[] in bytes */ - int szUncompressed = 0; /* Size of data before compression */ - u8 *aFree = 0; /* Free this before returning */ - u32 iCrc32 = 0; /* crc32 of uncompressed data */ - - char *zName = 0; /* Path (name) of new entry */ - int nName = 0; /* Size of zName in bytes */ - char *zFree = 0; /* Free this before returning */ - int nByte; - - memset(&e, 0, sizeof(e)); - p = (ZipfileCtx*)sqlite3_aggregate_context(pCtx, sizeof(ZipfileCtx)); - if( p==0 ) return; - - /* Martial the arguments into stack variables */ - if( nVal!=2 && nVal!=4 && nVal!=5 ){ - zErr = sqlite3_mprintf("wrong number of arguments to function zipfile()"); - rc = SQLITE_ERROR; - goto zipfile_step_out; - } - pName = apVal[0]; - if( nVal==2 ){ - pData = apVal[1]; - }else{ - pMode = apVal[1]; - pMtime = apVal[2]; - pData = apVal[3]; - if( nVal==5 ){ - pMethod = apVal[4]; - } - } - - /* Check that the 'name' parameter looks ok. */ - zName = (char*)sqlite3_value_text(pName); - nName = sqlite3_value_bytes(pName); - if( zName==0 ){ - zErr = sqlite3_mprintf("first argument to zipfile() must be non-NULL"); - rc = SQLITE_ERROR; - goto zipfile_step_out; - } - - /* Inspect the 'method' parameter. This must be either 0 (store), 8 (use - ** deflate compression) or NULL (choose automatically). */ - if( pMethod && SQLITE_NULL!=sqlite3_value_type(pMethod) ){ - iMethod = (int)sqlite3_value_int64(pMethod); - if( iMethod!=0 && iMethod!=8 ){ - zErr = sqlite3_mprintf("illegal method value: %d", iMethod); - rc = SQLITE_ERROR; - goto zipfile_step_out; - } - } - - /* Now inspect the data. If this is NULL, then the new entry must be a - ** directory. Otherwise, figure out whether or not the data should - ** be deflated or simply stored in the zip archive. */ - if( sqlite3_value_type(pData)==SQLITE_NULL ){ - bIsDir = 1; - iMethod = 0; - }else{ - aData = sqlite3_value_blob(pData); - szUncompressed = nData = sqlite3_value_bytes(pData); - iCrc32 = crc32(0, aData, nData); - if( iMethod<0 || iMethod==8 ){ - int nOut = 0; - rc = zipfileDeflate(aData, nData, &aFree, &nOut, &zErr); - if( rc!=SQLITE_OK ){ - goto zipfile_step_out; - } - if( iMethod==8 || nOut0 && zName[nName-1]=='/' ){ - zErr = sqlite3_mprintf("non-directory name must not end with /"); - rc = SQLITE_ERROR; - goto zipfile_step_out; - } - }else{ - if( nName==0 || zName[nName-1]!='/' ){ - zName = zFree = sqlite3_mprintf("%s/", zName); - if( zName==0 ){ - rc = SQLITE_NOMEM; - goto zipfile_step_out; - } - nName = (int)strlen(zName); - }else{ - while( nName>1 && zName[nName-2]=='/' ) nName--; - } - } - - /* Assemble the ZipfileEntry object for the new zip archive entry */ - e.cds.iVersionMadeBy = ZIPFILE_NEWENTRY_MADEBY; - e.cds.iVersionExtract = ZIPFILE_NEWENTRY_REQUIRED; - e.cds.flags = ZIPFILE_NEWENTRY_FLAGS; - e.cds.iCompression = (u16)iMethod; - zipfileMtimeToDos(&e.cds, (u32)e.mUnixTime); - e.cds.crc32 = iCrc32; - e.cds.szCompressed = nData; - e.cds.szUncompressed = szUncompressed; - e.cds.iExternalAttr = (mode<<16); - e.cds.iOffset = p->body.n; - e.cds.nFile = (u16)nName; - e.cds.zFile = zName; - - /* Append the LFH to the body of the new archive */ - nByte = ZIPFILE_LFH_FIXED_SZ + e.cds.nFile + 9; - if( (rc = zipfileBufferGrow(&p->body, nByte)) ) goto zipfile_step_out; - p->body.n += zipfileSerializeLFH(&e, &p->body.a[p->body.n]); - - /* Append the data to the body of the new archive */ - if( nData>0 ){ - if( (rc = zipfileBufferGrow(&p->body, nData)) ) goto zipfile_step_out; - memcpy(&p->body.a[p->body.n], aData, nData); - p->body.n += nData; - } - - /* Append the CDS record to the directory of the new archive */ - nByte = ZIPFILE_CDS_FIXED_SZ + e.cds.nFile + 9; - if( (rc = zipfileBufferGrow(&p->cds, nByte)) ) goto zipfile_step_out; - p->cds.n += zipfileSerializeCDS(&e, &p->cds.a[p->cds.n]); - - /* Increment the count of entries in the archive */ - p->nEntry++; - - zipfile_step_out: - sqlite3_free(aFree); - sqlite3_free(zFree); - if( rc ){ - if( zErr ){ - sqlite3_result_error(pCtx, zErr, -1); - }else{ - sqlite3_result_error_code(pCtx, rc); - } - } - sqlite3_free(zErr); -} - -/* -** xFinalize() callback for zipfile aggregate function. -*/ -void zipfileFinal(sqlite3_context *pCtx){ - ZipfileCtx *p; - ZipfileEOCD eocd; - sqlite3_int64 nZip; - u8 *aZip; - - p = (ZipfileCtx*)sqlite3_aggregate_context(pCtx, sizeof(ZipfileCtx)); - if( p==0 ) return; - if( p->nEntry>0 ){ - memset(&eocd, 0, sizeof(eocd)); - eocd.nEntry = (u16)p->nEntry; - eocd.nEntryTotal = (u16)p->nEntry; - eocd.nSize = p->cds.n; - eocd.iOffset = p->body.n; - - nZip = p->body.n + p->cds.n + ZIPFILE_EOCD_FIXED_SZ; - aZip = (u8*)sqlite3_malloc64(nZip); - if( aZip==0 ){ - sqlite3_result_error_nomem(pCtx); - }else{ - memcpy(aZip, p->body.a, p->body.n); - memcpy(&aZip[p->body.n], p->cds.a, p->cds.n); - zipfileSerializeEOCD(&eocd, &aZip[p->body.n + p->cds.n]); - sqlite3_result_blob(pCtx, aZip, (int)nZip, zipfileFree); - } - } - - sqlite3_free(p->body.a); - sqlite3_free(p->cds.a); -} - - -/* -** Register the "zipfile" virtual table. -*/ -static int zipfileRegister(sqlite3 *db){ - static sqlite3_module zipfileModule = { - 1, /* iVersion */ - zipfileConnect, /* xCreate */ - zipfileConnect, /* xConnect */ - zipfileBestIndex, /* xBestIndex */ - zipfileDisconnect, /* xDisconnect */ - zipfileDisconnect, /* xDestroy */ - zipfileOpen, /* xOpen - open a cursor */ - zipfileClose, /* xClose - close a cursor */ - zipfileFilter, /* xFilter - configure scan constraints */ - zipfileNext, /* xNext - advance a cursor */ - zipfileEof, /* xEof - check for end of scan */ - zipfileColumn, /* xColumn - read data */ - 0, /* xRowid - read data */ - zipfileUpdate, /* xUpdate */ - zipfileBegin, /* xBegin */ - 0, /* xSync */ - zipfileCommit, /* xCommit */ - zipfileRollback, /* xRollback */ - zipfileFindFunction, /* xFindMethod */ - 0, /* xRename */ - }; - - int rc = sqlite3_create_module(db, "zipfile" , &zipfileModule, 0); - if( rc==SQLITE_OK ) rc = sqlite3_overload_function(db, "zipfile_cds", -1); - if( rc==SQLITE_OK ){ - rc = sqlite3_create_function(db, "zipfile", -1, SQLITE_UTF8, 0, 0, - zipfileStep, zipfileFinal - ); - } - return rc; -} -#else /* SQLITE_OMIT_VIRTUALTABLE */ -# define zipfileRegister(x) SQLITE_OK -#endif - -#ifdef _WIN32 - -#endif -int sqlite3_zipfile_init( - sqlite3 *db, - char **pzErrMsg, - const sqlite3_api_routines *pApi -){ - SQLITE_EXTENSION_INIT2(pApi); - (void)pzErrMsg; /* Unused parameter */ - return zipfileRegister(db); -} - -/************************* End ../ext/misc/zipfile.c ********************/ -/************************* Begin ../ext/misc/sqlar.c ******************/ -/* -** 2017-12-17 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -****************************************************************************** -** -** Utility functions sqlar_compress() and sqlar_uncompress(). Useful -** for working with sqlar archives and used by the shell tool's built-in -** sqlar support. -*/ -/* #include "sqlite3ext.h" */ -SQLITE_EXTENSION_INIT1 -#include -#include - -/* -** Implementation of the "sqlar_compress(X)" SQL function. -** -** If the type of X is SQLITE_BLOB, and compressing that blob using -** zlib utility function compress() yields a smaller blob, return the -** compressed blob. Otherwise, return a copy of X. -** -** SQLar uses the "zlib format" for compressed content. The zlib format -** contains a two-byte identification header and a four-byte checksum at -** the end. This is different from ZIP which uses the raw deflate format. -** -** Future enhancements to SQLar might add support for new compression formats. -** If so, those new formats will be identified by alternative headers in the -** compressed data. -*/ -static void sqlarCompressFunc( - sqlite3_context *context, - int argc, - sqlite3_value **argv -){ - assert( argc==1 ); - if( sqlite3_value_type(argv[0])==SQLITE_BLOB ){ - const Bytef *pData = sqlite3_value_blob(argv[0]); - uLong nData = sqlite3_value_bytes(argv[0]); - uLongf nOut = compressBound(nData); - Bytef *pOut; - - pOut = (Bytef*)sqlite3_malloc(nOut); - if( pOut==0 ){ - sqlite3_result_error_nomem(context); - return; - }else{ - if( Z_OK!=compress(pOut, &nOut, pData, nData) ){ - sqlite3_result_error(context, "error in compress()", -1); - }else if( nOut -#include -#include - -#ifndef SQLITE_OMIT_VIRTUALTABLE - -/* typedef sqlite3_int64 i64; */ -/* typedef sqlite3_uint64 u64; */ - -typedef struct IdxColumn IdxColumn; -typedef struct IdxConstraint IdxConstraint; -typedef struct IdxScan IdxScan; -typedef struct IdxStatement IdxStatement; -typedef struct IdxTable IdxTable; -typedef struct IdxWrite IdxWrite; - -#define STRLEN (int)strlen - -/* -** A temp table name that we assume no user database will actually use. -** If this assumption proves incorrect triggers on the table with the -** conflicting name will be ignored. -*/ -#define UNIQUE_TABLE_NAME "t592690916721053953805701627921227776" - -/* -** A single constraint. Equivalent to either "col = ?" or "col < ?" (or -** any other type of single-ended range constraint on a column). -** -** pLink: -** Used to temporarily link IdxConstraint objects into lists while -** creating candidate indexes. -*/ -struct IdxConstraint { - char *zColl; /* Collation sequence */ - int bRange; /* True for range, false for eq */ - int iCol; /* Constrained table column */ - int bFlag; /* Used by idxFindCompatible() */ - int bDesc; /* True if ORDER BY DESC */ - IdxConstraint *pNext; /* Next constraint in pEq or pRange list */ - IdxConstraint *pLink; /* See above */ -}; - -/* -** A single scan of a single table. -*/ -struct IdxScan { - IdxTable *pTab; /* Associated table object */ - int iDb; /* Database containing table zTable */ - i64 covering; /* Mask of columns required for cov. index */ - IdxConstraint *pOrder; /* ORDER BY columns */ - IdxConstraint *pEq; /* List of == constraints */ - IdxConstraint *pRange; /* List of < constraints */ - IdxScan *pNextScan; /* Next IdxScan object for same analysis */ -}; - -/* -** Information regarding a single database table. Extracted from -** "PRAGMA table_info" by function idxGetTableInfo(). -*/ -struct IdxColumn { - char *zName; - char *zColl; - int iPk; -}; -struct IdxTable { - int nCol; - char *zName; /* Table name */ - IdxColumn *aCol; - IdxTable *pNext; /* Next table in linked list of all tables */ -}; - -/* -** An object of the following type is created for each unique table/write-op -** seen. The objects are stored in a singly-linked list beginning at -** sqlite3expert.pWrite. -*/ -struct IdxWrite { - IdxTable *pTab; - int eOp; /* SQLITE_UPDATE, DELETE or INSERT */ - IdxWrite *pNext; -}; - -/* -** Each statement being analyzed is represented by an instance of this -** structure. -*/ -struct IdxStatement { - int iId; /* Statement number */ - char *zSql; /* SQL statement */ - char *zIdx; /* Indexes */ - char *zEQP; /* Plan */ - IdxStatement *pNext; -}; - - -/* -** A hash table for storing strings. With space for a payload string -** with each entry. Methods are: -** -** idxHashInit() -** idxHashClear() -** idxHashAdd() -** idxHashSearch() -*/ -#define IDX_HASH_SIZE 1023 -typedef struct IdxHashEntry IdxHashEntry; -typedef struct IdxHash IdxHash; -struct IdxHashEntry { - char *zKey; /* nul-terminated key */ - char *zVal; /* nul-terminated value string */ - char *zVal2; /* nul-terminated value string 2 */ - IdxHashEntry *pHashNext; /* Next entry in same hash bucket */ - IdxHashEntry *pNext; /* Next entry in hash */ -}; -struct IdxHash { - IdxHashEntry *pFirst; - IdxHashEntry *aHash[IDX_HASH_SIZE]; -}; - -/* -** sqlite3expert object. -*/ -struct sqlite3expert { - int iSample; /* Percentage of tables to sample for stat1 */ - sqlite3 *db; /* User database */ - sqlite3 *dbm; /* In-memory db for this analysis */ - sqlite3 *dbv; /* Vtab schema for this analysis */ - IdxTable *pTable; /* List of all IdxTable objects */ - IdxScan *pScan; /* List of scan objects */ - IdxWrite *pWrite; /* List of write objects */ - IdxStatement *pStatement; /* List of IdxStatement objects */ - int bRun; /* True once analysis has run */ - char **pzErrmsg; - int rc; /* Error code from whereinfo hook */ - IdxHash hIdx; /* Hash containing all candidate indexes */ - char *zCandidates; /* For EXPERT_REPORT_CANDIDATES */ -}; - - -/* -** Allocate and return nByte bytes of zeroed memory using sqlite3_malloc(). -** If the allocation fails, set *pRc to SQLITE_NOMEM and return NULL. -*/ -static void *idxMalloc(int *pRc, int nByte){ - void *pRet; - assert( *pRc==SQLITE_OK ); - assert( nByte>0 ); - pRet = sqlite3_malloc(nByte); - if( pRet ){ - memset(pRet, 0, nByte); - }else{ - *pRc = SQLITE_NOMEM; - } - return pRet; -} - -/* -** Initialize an IdxHash hash table. -*/ -static void idxHashInit(IdxHash *pHash){ - memset(pHash, 0, sizeof(IdxHash)); -} - -/* -** Reset an IdxHash hash table. -*/ -static void idxHashClear(IdxHash *pHash){ - int i; - for(i=0; iaHash[i]; pEntry; pEntry=pNext){ - pNext = pEntry->pHashNext; - sqlite3_free(pEntry->zVal2); - sqlite3_free(pEntry); - } - } - memset(pHash, 0, sizeof(IdxHash)); -} - -/* -** Return the index of the hash bucket that the string specified by the -** arguments to this function belongs. -*/ -static int idxHashString(const char *z, int n){ - unsigned int ret = 0; - int i; - for(i=0; i=0 ); - for(pEntry=pHash->aHash[iHash]; pEntry; pEntry=pEntry->pHashNext){ - if( STRLEN(pEntry->zKey)==nKey && 0==memcmp(pEntry->zKey, zKey, nKey) ){ - return 1; - } - } - pEntry = idxMalloc(pRc, sizeof(IdxHashEntry) + nKey+1 + nVal+1); - if( pEntry ){ - pEntry->zKey = (char*)&pEntry[1]; - memcpy(pEntry->zKey, zKey, nKey); - if( zVal ){ - pEntry->zVal = &pEntry->zKey[nKey+1]; - memcpy(pEntry->zVal, zVal, nVal); - } - pEntry->pHashNext = pHash->aHash[iHash]; - pHash->aHash[iHash] = pEntry; - - pEntry->pNext = pHash->pFirst; - pHash->pFirst = pEntry; - } - return 0; -} - -/* -** If zKey/nKey is present in the hash table, return a pointer to the -** hash-entry object. -*/ -static IdxHashEntry *idxHashFind(IdxHash *pHash, const char *zKey, int nKey){ - int iHash; - IdxHashEntry *pEntry; - if( nKey<0 ) nKey = STRLEN(zKey); - iHash = idxHashString(zKey, nKey); - assert( iHash>=0 ); - for(pEntry=pHash->aHash[iHash]; pEntry; pEntry=pEntry->pHashNext){ - if( STRLEN(pEntry->zKey)==nKey && 0==memcmp(pEntry->zKey, zKey, nKey) ){ - return pEntry; - } - } - return 0; -} - -/* -** If the hash table contains an entry with a key equal to the string -** passed as the final two arguments to this function, return a pointer -** to the payload string. Otherwise, if zKey/nKey is not present in the -** hash table, return NULL. -*/ -static const char *idxHashSearch(IdxHash *pHash, const char *zKey, int nKey){ - IdxHashEntry *pEntry = idxHashFind(pHash, zKey, nKey); - if( pEntry ) return pEntry->zVal; - return 0; -} - -/* -** Allocate and return a new IdxConstraint object. Set the IdxConstraint.zColl -** variable to point to a copy of nul-terminated string zColl. -*/ -static IdxConstraint *idxNewConstraint(int *pRc, const char *zColl){ - IdxConstraint *pNew; - int nColl = STRLEN(zColl); - - assert( *pRc==SQLITE_OK ); - pNew = (IdxConstraint*)idxMalloc(pRc, sizeof(IdxConstraint) * nColl + 1); - if( pNew ){ - pNew->zColl = (char*)&pNew[1]; - memcpy(pNew->zColl, zColl, nColl+1); - } - return pNew; -} - -/* -** An error associated with database handle db has just occurred. Pass -** the error message to callback function xOut. -*/ -static void idxDatabaseError( - sqlite3 *db, /* Database handle */ - char **pzErrmsg /* Write error here */ -){ - *pzErrmsg = sqlite3_mprintf("%s", sqlite3_errmsg(db)); -} - -/* -** Prepare an SQL statement. -*/ -static int idxPrepareStmt( - sqlite3 *db, /* Database handle to compile against */ - sqlite3_stmt **ppStmt, /* OUT: Compiled SQL statement */ - char **pzErrmsg, /* OUT: sqlite3_malloc()ed error message */ - const char *zSql /* SQL statement to compile */ -){ - int rc = sqlite3_prepare_v2(db, zSql, -1, ppStmt, 0); - if( rc!=SQLITE_OK ){ - *ppStmt = 0; - idxDatabaseError(db, pzErrmsg); - } - return rc; -} - -/* -** Prepare an SQL statement using the results of a printf() formatting. -*/ -static int idxPrintfPrepareStmt( - sqlite3 *db, /* Database handle to compile against */ - sqlite3_stmt **ppStmt, /* OUT: Compiled SQL statement */ - char **pzErrmsg, /* OUT: sqlite3_malloc()ed error message */ - const char *zFmt, /* printf() format of SQL statement */ - ... /* Trailing printf() arguments */ -){ - va_list ap; - int rc; - char *zSql; - va_start(ap, zFmt); - zSql = sqlite3_vmprintf(zFmt, ap); - if( zSql==0 ){ - rc = SQLITE_NOMEM; - }else{ - rc = idxPrepareStmt(db, ppStmt, pzErrmsg, zSql); - sqlite3_free(zSql); - } - va_end(ap); - return rc; -} - - -/************************************************************************* -** Beginning of virtual table implementation. -*/ -typedef struct ExpertVtab ExpertVtab; -struct ExpertVtab { - sqlite3_vtab base; - IdxTable *pTab; - sqlite3expert *pExpert; -}; - -typedef struct ExpertCsr ExpertCsr; -struct ExpertCsr { - sqlite3_vtab_cursor base; - sqlite3_stmt *pData; -}; - -static char *expertDequote(const char *zIn){ - int n = STRLEN(zIn); - char *zRet = sqlite3_malloc(n); - - assert( zIn[0]=='\'' ); - assert( zIn[n-1]=='\'' ); - - if( zRet ){ - int iOut = 0; - int iIn = 0; - for(iIn=1; iIn<(n-1); iIn++){ - if( zIn[iIn]=='\'' ){ - assert( zIn[iIn+1]=='\'' ); - iIn++; - } - zRet[iOut++] = zIn[iIn]; - } - zRet[iOut] = '\0'; - } - - return zRet; -} - -/* -** This function is the implementation of both the xConnect and xCreate -** methods of the r-tree virtual table. -** -** argv[0] -> module name -** argv[1] -> database name -** argv[2] -> table name -** argv[...] -> column names... -*/ -static int expertConnect( - sqlite3 *db, - void *pAux, - int argc, const char *const*argv, - sqlite3_vtab **ppVtab, - char **pzErr -){ - sqlite3expert *pExpert = (sqlite3expert*)pAux; - ExpertVtab *p = 0; - int rc; - - if( argc!=4 ){ - *pzErr = sqlite3_mprintf("internal error!"); - rc = SQLITE_ERROR; - }else{ - char *zCreateTable = expertDequote(argv[3]); - if( zCreateTable ){ - rc = sqlite3_declare_vtab(db, zCreateTable); - if( rc==SQLITE_OK ){ - p = idxMalloc(&rc, sizeof(ExpertVtab)); - } - if( rc==SQLITE_OK ){ - p->pExpert = pExpert; - p->pTab = pExpert->pTable; - assert( sqlite3_stricmp(p->pTab->zName, argv[2])==0 ); - } - sqlite3_free(zCreateTable); - }else{ - rc = SQLITE_NOMEM; - } - } - - *ppVtab = (sqlite3_vtab*)p; - return rc; -} - -static int expertDisconnect(sqlite3_vtab *pVtab){ - ExpertVtab *p = (ExpertVtab*)pVtab; - sqlite3_free(p); - return SQLITE_OK; -} - -static int expertBestIndex(sqlite3_vtab *pVtab, sqlite3_index_info *pIdxInfo){ - ExpertVtab *p = (ExpertVtab*)pVtab; - int rc = SQLITE_OK; - int n = 0; - IdxScan *pScan; - const int opmask = - SQLITE_INDEX_CONSTRAINT_EQ | SQLITE_INDEX_CONSTRAINT_GT | - SQLITE_INDEX_CONSTRAINT_LT | SQLITE_INDEX_CONSTRAINT_GE | - SQLITE_INDEX_CONSTRAINT_LE; - - pScan = idxMalloc(&rc, sizeof(IdxScan)); - if( pScan ){ - int i; - - /* Link the new scan object into the list */ - pScan->pTab = p->pTab; - pScan->pNextScan = p->pExpert->pScan; - p->pExpert->pScan = pScan; - - /* Add the constraints to the IdxScan object */ - for(i=0; inConstraint; i++){ - struct sqlite3_index_constraint *pCons = &pIdxInfo->aConstraint[i]; - if( pCons->usable - && pCons->iColumn>=0 - && p->pTab->aCol[pCons->iColumn].iPk==0 - && (pCons->op & opmask) - ){ - IdxConstraint *pNew; - const char *zColl = sqlite3_vtab_collation(pIdxInfo, i); - pNew = idxNewConstraint(&rc, zColl); - if( pNew ){ - pNew->iCol = pCons->iColumn; - if( pCons->op==SQLITE_INDEX_CONSTRAINT_EQ ){ - pNew->pNext = pScan->pEq; - pScan->pEq = pNew; - }else{ - pNew->bRange = 1; - pNew->pNext = pScan->pRange; - pScan->pRange = pNew; - } - } - n++; - pIdxInfo->aConstraintUsage[i].argvIndex = n; - } - } - - /* Add the ORDER BY to the IdxScan object */ - for(i=pIdxInfo->nOrderBy-1; i>=0; i--){ - int iCol = pIdxInfo->aOrderBy[i].iColumn; - if( iCol>=0 ){ - IdxConstraint *pNew = idxNewConstraint(&rc, p->pTab->aCol[iCol].zColl); - if( pNew ){ - pNew->iCol = iCol; - pNew->bDesc = pIdxInfo->aOrderBy[i].desc; - pNew->pNext = pScan->pOrder; - pNew->pLink = pScan->pOrder; - pScan->pOrder = pNew; - n++; - } - } - } - } - - pIdxInfo->estimatedCost = 1000000.0 / (n+1); - return rc; -} - -static int expertUpdate( - sqlite3_vtab *pVtab, - int nData, - sqlite3_value **azData, - sqlite_int64 *pRowid -){ - (void)pVtab; - (void)nData; - (void)azData; - (void)pRowid; - return SQLITE_OK; -} - -/* -** Virtual table module xOpen method. -*/ -static int expertOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){ - int rc = SQLITE_OK; - ExpertCsr *pCsr; - (void)pVTab; - pCsr = idxMalloc(&rc, sizeof(ExpertCsr)); - *ppCursor = (sqlite3_vtab_cursor*)pCsr; - return rc; -} - -/* -** Virtual table module xClose method. -*/ -static int expertClose(sqlite3_vtab_cursor *cur){ - ExpertCsr *pCsr = (ExpertCsr*)cur; - sqlite3_finalize(pCsr->pData); - sqlite3_free(pCsr); - return SQLITE_OK; -} - -/* -** Virtual table module xEof method. -** -** Return non-zero if the cursor does not currently point to a valid -** record (i.e if the scan has finished), or zero otherwise. -*/ -static int expertEof(sqlite3_vtab_cursor *cur){ - ExpertCsr *pCsr = (ExpertCsr*)cur; - return pCsr->pData==0; -} - -/* -** Virtual table module xNext method. -*/ -static int expertNext(sqlite3_vtab_cursor *cur){ - ExpertCsr *pCsr = (ExpertCsr*)cur; - int rc = SQLITE_OK; - - assert( pCsr->pData ); - rc = sqlite3_step(pCsr->pData); - if( rc!=SQLITE_ROW ){ - rc = sqlite3_finalize(pCsr->pData); - pCsr->pData = 0; - }else{ - rc = SQLITE_OK; - } - - return rc; -} - -/* -** Virtual table module xRowid method. -*/ -static int expertRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){ - (void)cur; - *pRowid = 0; - return SQLITE_OK; -} - -/* -** Virtual table module xColumn method. -*/ -static int expertColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i){ - ExpertCsr *pCsr = (ExpertCsr*)cur; - sqlite3_value *pVal; - pVal = sqlite3_column_value(pCsr->pData, i); - if( pVal ){ - sqlite3_result_value(ctx, pVal); - } - return SQLITE_OK; -} - -/* -** Virtual table module xFilter method. -*/ -static int expertFilter( - sqlite3_vtab_cursor *cur, - int idxNum, const char *idxStr, - int argc, sqlite3_value **argv -){ - ExpertCsr *pCsr = (ExpertCsr*)cur; - ExpertVtab *pVtab = (ExpertVtab*)(cur->pVtab); - sqlite3expert *pExpert = pVtab->pExpert; - int rc; - - (void)idxNum; - (void)idxStr; - (void)argc; - (void)argv; - rc = sqlite3_finalize(pCsr->pData); - pCsr->pData = 0; - if( rc==SQLITE_OK ){ - rc = idxPrintfPrepareStmt(pExpert->db, &pCsr->pData, &pVtab->base.zErrMsg, - "SELECT * FROM main.%Q WHERE sample()", pVtab->pTab->zName - ); - } - - if( rc==SQLITE_OK ){ - rc = expertNext(cur); - } - return rc; -} - -static int idxRegisterVtab(sqlite3expert *p){ - static sqlite3_module expertModule = { - 2, /* iVersion */ - expertConnect, /* xCreate - create a table */ - expertConnect, /* xConnect - connect to an existing table */ - expertBestIndex, /* xBestIndex - Determine search strategy */ - expertDisconnect, /* xDisconnect - Disconnect from a table */ - expertDisconnect, /* xDestroy - Drop a table */ - expertOpen, /* xOpen - open a cursor */ - expertClose, /* xClose - close a cursor */ - expertFilter, /* xFilter - configure scan constraints */ - expertNext, /* xNext - advance a cursor */ - expertEof, /* xEof */ - expertColumn, /* xColumn - read data */ - expertRowid, /* xRowid - read data */ - expertUpdate, /* xUpdate - write data */ - 0, /* xBegin - begin transaction */ - 0, /* xSync - sync transaction */ - 0, /* xCommit - commit transaction */ - 0, /* xRollback - rollback transaction */ - 0, /* xFindFunction - function overloading */ - 0, /* xRename - rename the table */ - 0, /* xSavepoint */ - 0, /* xRelease */ - 0, /* xRollbackTo */ - 0, /* xShadowName */ - }; - - return sqlite3_create_module(p->dbv, "expert", &expertModule, (void*)p); -} -/* -** End of virtual table implementation. -*************************************************************************/ -/* -** Finalize SQL statement pStmt. If (*pRc) is SQLITE_OK when this function -** is called, set it to the return value of sqlite3_finalize() before -** returning. Otherwise, discard the sqlite3_finalize() return value. -*/ -static void idxFinalize(int *pRc, sqlite3_stmt *pStmt){ - int rc = sqlite3_finalize(pStmt); - if( *pRc==SQLITE_OK ) *pRc = rc; -} - -/* -** Attempt to allocate an IdxTable structure corresponding to table zTab -** in the main database of connection db. If successful, set (*ppOut) to -** point to the new object and return SQLITE_OK. Otherwise, return an -** SQLite error code and set (*ppOut) to NULL. In this case *pzErrmsg may be -** set to point to an error string. -** -** It is the responsibility of the caller to eventually free either the -** IdxTable object or error message using sqlite3_free(). -*/ -static int idxGetTableInfo( - sqlite3 *db, /* Database connection to read details from */ - const char *zTab, /* Table name */ - IdxTable **ppOut, /* OUT: New object (if successful) */ - char **pzErrmsg /* OUT: Error message (if not) */ -){ - sqlite3_stmt *p1 = 0; - int nCol = 0; - int nTab = STRLEN(zTab); - int nByte = sizeof(IdxTable) + nTab + 1; - IdxTable *pNew = 0; - int rc, rc2; - char *pCsr = 0; - - rc = idxPrintfPrepareStmt(db, &p1, pzErrmsg, "PRAGMA table_info=%Q", zTab); - while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(p1) ){ - const char *zCol = (const char*)sqlite3_column_text(p1, 1); - nByte += 1 + STRLEN(zCol); - rc = sqlite3_table_column_metadata( - db, "main", zTab, zCol, 0, &zCol, 0, 0, 0 - ); - nByte += 1 + STRLEN(zCol); - nCol++; - } - rc2 = sqlite3_reset(p1); - if( rc==SQLITE_OK ) rc = rc2; - - nByte += sizeof(IdxColumn) * nCol; - if( rc==SQLITE_OK ){ - pNew = idxMalloc(&rc, nByte); - } - if( rc==SQLITE_OK ){ - pNew->aCol = (IdxColumn*)&pNew[1]; - pNew->nCol = nCol; - pCsr = (char*)&pNew->aCol[nCol]; - } - - nCol = 0; - while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(p1) ){ - const char *zCol = (const char*)sqlite3_column_text(p1, 1); - int nCopy = STRLEN(zCol) + 1; - pNew->aCol[nCol].zName = pCsr; - pNew->aCol[nCol].iPk = sqlite3_column_int(p1, 5); - memcpy(pCsr, zCol, nCopy); - pCsr += nCopy; - - rc = sqlite3_table_column_metadata( - db, "main", zTab, zCol, 0, &zCol, 0, 0, 0 - ); - if( rc==SQLITE_OK ){ - nCopy = STRLEN(zCol) + 1; - pNew->aCol[nCol].zColl = pCsr; - memcpy(pCsr, zCol, nCopy); - pCsr += nCopy; - } - - nCol++; - } - idxFinalize(&rc, p1); - - if( rc!=SQLITE_OK ){ - sqlite3_free(pNew); - pNew = 0; - }else{ - pNew->zName = pCsr; - memcpy(pNew->zName, zTab, nTab+1); - } - - *ppOut = pNew; - return rc; -} - -/* -** This function is a no-op if *pRc is set to anything other than -** SQLITE_OK when it is called. -** -** If *pRc is initially set to SQLITE_OK, then the text specified by -** the printf() style arguments is appended to zIn and the result returned -** in a buffer allocated by sqlite3_malloc(). sqlite3_free() is called on -** zIn before returning. -*/ -static char *idxAppendText(int *pRc, char *zIn, const char *zFmt, ...){ - va_list ap; - char *zAppend = 0; - char *zRet = 0; - int nIn = zIn ? STRLEN(zIn) : 0; - int nAppend = 0; - va_start(ap, zFmt); - if( *pRc==SQLITE_OK ){ - zAppend = sqlite3_vmprintf(zFmt, ap); - if( zAppend ){ - nAppend = STRLEN(zAppend); - zRet = (char*)sqlite3_malloc(nIn + nAppend + 1); - } - if( zAppend && zRet ){ - if( nIn ) memcpy(zRet, zIn, nIn); - memcpy(&zRet[nIn], zAppend, nAppend+1); - }else{ - sqlite3_free(zRet); - zRet = 0; - *pRc = SQLITE_NOMEM; - } - sqlite3_free(zAppend); - sqlite3_free(zIn); - } - va_end(ap); - return zRet; -} - -/* -** Return true if zId must be quoted in order to use it as an SQL -** identifier, or false otherwise. -*/ -static int idxIdentifierRequiresQuotes(const char *zId){ - int i; - for(i=0; zId[i]; i++){ - if( !(zId[i]=='_') - && !(zId[i]>='0' && zId[i]<='9') - && !(zId[i]>='a' && zId[i]<='z') - && !(zId[i]>='A' && zId[i]<='Z') - ){ - return 1; - } - } - return 0; -} - -/* -** This function appends an index column definition suitable for constraint -** pCons to the string passed as zIn and returns the result. -*/ -static char *idxAppendColDefn( - int *pRc, /* IN/OUT: Error code */ - char *zIn, /* Column defn accumulated so far */ - IdxTable *pTab, /* Table index will be created on */ - IdxConstraint *pCons -){ - char *zRet = zIn; - IdxColumn *p = &pTab->aCol[pCons->iCol]; - if( zRet ) zRet = idxAppendText(pRc, zRet, ", "); - - if( idxIdentifierRequiresQuotes(p->zName) ){ - zRet = idxAppendText(pRc, zRet, "%Q", p->zName); - }else{ - zRet = idxAppendText(pRc, zRet, "%s", p->zName); - } - - if( sqlite3_stricmp(p->zColl, pCons->zColl) ){ - if( idxIdentifierRequiresQuotes(pCons->zColl) ){ - zRet = idxAppendText(pRc, zRet, " COLLATE %Q", pCons->zColl); - }else{ - zRet = idxAppendText(pRc, zRet, " COLLATE %s", pCons->zColl); - } - } - - if( pCons->bDesc ){ - zRet = idxAppendText(pRc, zRet, " DESC"); - } - return zRet; -} - -/* -** Search database dbm for an index compatible with the one idxCreateFromCons() -** would create from arguments pScan, pEq and pTail. If no error occurs and -** such an index is found, return non-zero. Or, if no such index is found, -** return zero. -** -** If an error occurs, set *pRc to an SQLite error code and return zero. -*/ -static int idxFindCompatible( - int *pRc, /* OUT: Error code */ - sqlite3* dbm, /* Database to search */ - IdxScan *pScan, /* Scan for table to search for index on */ - IdxConstraint *pEq, /* List of == constraints */ - IdxConstraint *pTail /* List of range constraints */ -){ - const char *zTbl = pScan->pTab->zName; - sqlite3_stmt *pIdxList = 0; - IdxConstraint *pIter; - int nEq = 0; /* Number of elements in pEq */ - int rc; - - /* Count the elements in list pEq */ - for(pIter=pEq; pIter; pIter=pIter->pLink) nEq++; - - rc = idxPrintfPrepareStmt(dbm, &pIdxList, 0, "PRAGMA index_list=%Q", zTbl); - while( rc==SQLITE_OK && sqlite3_step(pIdxList)==SQLITE_ROW ){ - int bMatch = 1; - IdxConstraint *pT = pTail; - sqlite3_stmt *pInfo = 0; - const char *zIdx = (const char*)sqlite3_column_text(pIdxList, 1); - - /* Zero the IdxConstraint.bFlag values in the pEq list */ - for(pIter=pEq; pIter; pIter=pIter->pLink) pIter->bFlag = 0; - - rc = idxPrintfPrepareStmt(dbm, &pInfo, 0, "PRAGMA index_xInfo=%Q", zIdx); - while( rc==SQLITE_OK && sqlite3_step(pInfo)==SQLITE_ROW ){ - int iIdx = sqlite3_column_int(pInfo, 0); - int iCol = sqlite3_column_int(pInfo, 1); - const char *zColl = (const char*)sqlite3_column_text(pInfo, 4); - - if( iIdxpLink){ - if( pIter->bFlag ) continue; - if( pIter->iCol!=iCol ) continue; - if( sqlite3_stricmp(pIter->zColl, zColl) ) continue; - pIter->bFlag = 1; - break; - } - if( pIter==0 ){ - bMatch = 0; - break; - } - }else{ - if( pT ){ - if( pT->iCol!=iCol || sqlite3_stricmp(pT->zColl, zColl) ){ - bMatch = 0; - break; - } - pT = pT->pLink; - } - } - } - idxFinalize(&rc, pInfo); - - if( rc==SQLITE_OK && bMatch ){ - sqlite3_finalize(pIdxList); - return 1; - } - } - idxFinalize(&rc, pIdxList); - - *pRc = rc; - return 0; -} - -static int idxCreateFromCons( - sqlite3expert *p, - IdxScan *pScan, - IdxConstraint *pEq, - IdxConstraint *pTail -){ - sqlite3 *dbm = p->dbm; - int rc = SQLITE_OK; - if( (pEq || pTail) && 0==idxFindCompatible(&rc, dbm, pScan, pEq, pTail) ){ - IdxTable *pTab = pScan->pTab; - char *zCols = 0; - char *zIdx = 0; - IdxConstraint *pCons; - unsigned int h = 0; - const char *zFmt; - - for(pCons=pEq; pCons; pCons=pCons->pLink){ - zCols = idxAppendColDefn(&rc, zCols, pTab, pCons); - } - for(pCons=pTail; pCons; pCons=pCons->pLink){ - zCols = idxAppendColDefn(&rc, zCols, pTab, pCons); - } - - if( rc==SQLITE_OK ){ - /* Hash the list of columns to come up with a name for the index */ - const char *zTable = pScan->pTab->zName; - char *zName; /* Index name */ - int i; - for(i=0; zCols[i]; i++){ - h += ((h<<3) + zCols[i]); - } - zName = sqlite3_mprintf("%s_idx_%08x", zTable, h); - if( zName==0 ){ - rc = SQLITE_NOMEM; - }else{ - if( idxIdentifierRequiresQuotes(zTable) ){ - zFmt = "CREATE INDEX '%q' ON %Q(%s)"; - }else{ - zFmt = "CREATE INDEX %s ON %s(%s)"; - } - zIdx = sqlite3_mprintf(zFmt, zName, zTable, zCols); - if( !zIdx ){ - rc = SQLITE_NOMEM; - }else{ - rc = sqlite3_exec(dbm, zIdx, 0, 0, p->pzErrmsg); - idxHashAdd(&rc, &p->hIdx, zName, zIdx); - } - sqlite3_free(zName); - sqlite3_free(zIdx); - } - } - - sqlite3_free(zCols); - } - return rc; -} - -/* -** Return true if list pList (linked by IdxConstraint.pLink) contains -** a constraint compatible with *p. Otherwise return false. -*/ -static int idxFindConstraint(IdxConstraint *pList, IdxConstraint *p){ - IdxConstraint *pCmp; - for(pCmp=pList; pCmp; pCmp=pCmp->pLink){ - if( p->iCol==pCmp->iCol ) return 1; - } - return 0; -} - -static int idxCreateFromWhere( - sqlite3expert *p, - IdxScan *pScan, /* Create indexes for this scan */ - IdxConstraint *pTail /* range/ORDER BY constraints for inclusion */ -){ - IdxConstraint *p1 = 0; - IdxConstraint *pCon; - int rc; - - /* Gather up all the == constraints. */ - for(pCon=pScan->pEq; pCon; pCon=pCon->pNext){ - if( !idxFindConstraint(p1, pCon) && !idxFindConstraint(pTail, pCon) ){ - pCon->pLink = p1; - p1 = pCon; - } - } - - /* Create an index using the == constraints collected above. And the - ** range constraint/ORDER BY terms passed in by the caller, if any. */ - rc = idxCreateFromCons(p, pScan, p1, pTail); - - /* If no range/ORDER BY passed by the caller, create a version of the - ** index for each range constraint. */ - if( pTail==0 ){ - for(pCon=pScan->pRange; rc==SQLITE_OK && pCon; pCon=pCon->pNext){ - assert( pCon->pLink==0 ); - if( !idxFindConstraint(p1, pCon) && !idxFindConstraint(pTail, pCon) ){ - rc = idxCreateFromCons(p, pScan, p1, pCon); - } - } - } - - return rc; -} - -/* -** Create candidate indexes in database [dbm] based on the data in -** linked-list pScan. -*/ -static int idxCreateCandidates(sqlite3expert *p){ - int rc = SQLITE_OK; - IdxScan *pIter; - - for(pIter=p->pScan; pIter && rc==SQLITE_OK; pIter=pIter->pNextScan){ - rc = idxCreateFromWhere(p, pIter, 0); - if( rc==SQLITE_OK && pIter->pOrder ){ - rc = idxCreateFromWhere(p, pIter, pIter->pOrder); - } - } - - return rc; -} - -/* -** Free all elements of the linked list starting at pConstraint. -*/ -static void idxConstraintFree(IdxConstraint *pConstraint){ - IdxConstraint *pNext; - IdxConstraint *p; - - for(p=pConstraint; p; p=pNext){ - pNext = p->pNext; - sqlite3_free(p); - } -} - -/* -** Free all elements of the linked list starting from pScan up until pLast -** (pLast is not freed). -*/ -static void idxScanFree(IdxScan *pScan, IdxScan *pLast){ - IdxScan *p; - IdxScan *pNext; - for(p=pScan; p!=pLast; p=pNext){ - pNext = p->pNextScan; - idxConstraintFree(p->pOrder); - idxConstraintFree(p->pEq); - idxConstraintFree(p->pRange); - sqlite3_free(p); - } -} - -/* -** Free all elements of the linked list starting from pStatement up -** until pLast (pLast is not freed). -*/ -static void idxStatementFree(IdxStatement *pStatement, IdxStatement *pLast){ - IdxStatement *p; - IdxStatement *pNext; - for(p=pStatement; p!=pLast; p=pNext){ - pNext = p->pNext; - sqlite3_free(p->zEQP); - sqlite3_free(p->zIdx); - sqlite3_free(p); - } -} - -/* -** Free the linked list of IdxTable objects starting at pTab. -*/ -static void idxTableFree(IdxTable *pTab){ - IdxTable *pIter; - IdxTable *pNext; - for(pIter=pTab; pIter; pIter=pNext){ - pNext = pIter->pNext; - sqlite3_free(pIter); - } -} - -/* -** Free the linked list of IdxWrite objects starting at pTab. -*/ -static void idxWriteFree(IdxWrite *pTab){ - IdxWrite *pIter; - IdxWrite *pNext; - for(pIter=pTab; pIter; pIter=pNext){ - pNext = pIter->pNext; - sqlite3_free(pIter); - } -} - - - -/* -** This function is called after candidate indexes have been created. It -** runs all the queries to see which indexes they prefer, and populates -** IdxStatement.zIdx and IdxStatement.zEQP with the results. -*/ -int idxFindIndexes( - sqlite3expert *p, - char **pzErr /* OUT: Error message (sqlite3_malloc) */ -){ - IdxStatement *pStmt; - sqlite3 *dbm = p->dbm; - int rc = SQLITE_OK; - - IdxHash hIdx; - idxHashInit(&hIdx); - - for(pStmt=p->pStatement; rc==SQLITE_OK && pStmt; pStmt=pStmt->pNext){ - IdxHashEntry *pEntry; - sqlite3_stmt *pExplain = 0; - idxHashClear(&hIdx); - rc = idxPrintfPrepareStmt(dbm, &pExplain, pzErr, - "EXPLAIN QUERY PLAN %s", pStmt->zSql - ); - while( rc==SQLITE_OK && sqlite3_step(pExplain)==SQLITE_ROW ){ - /* int iId = sqlite3_column_int(pExplain, 0); */ - /* int iParent = sqlite3_column_int(pExplain, 1); */ - /* int iNotUsed = sqlite3_column_int(pExplain, 2); */ - const char *zDetail = (const char*)sqlite3_column_text(pExplain, 3); - int nDetail; - int i; - - if( !zDetail ) continue; - nDetail = STRLEN(zDetail); - - for(i=0; ihIdx, zIdx, nIdx); - if( zSql ){ - idxHashAdd(&rc, &hIdx, zSql, 0); - if( rc ) goto find_indexes_out; - } - break; - } - } - - if( zDetail[0]!='-' ){ - pStmt->zEQP = idxAppendText(&rc, pStmt->zEQP, "%s\n", zDetail); - } - } - - for(pEntry=hIdx.pFirst; pEntry; pEntry=pEntry->pNext){ - pStmt->zIdx = idxAppendText(&rc, pStmt->zIdx, "%s;\n", pEntry->zKey); - } - - idxFinalize(&rc, pExplain); - } - - find_indexes_out: - idxHashClear(&hIdx); - return rc; -} - -static int idxAuthCallback( - void *pCtx, - int eOp, - const char *z3, - const char *z4, - const char *zDb, - const char *zTrigger -){ - int rc = SQLITE_OK; - (void)z4; - (void)zTrigger; - if( eOp==SQLITE_INSERT || eOp==SQLITE_UPDATE || eOp==SQLITE_DELETE ){ - if( sqlite3_stricmp(zDb, "main")==0 ){ - sqlite3expert *p = (sqlite3expert*)pCtx; - IdxTable *pTab; - for(pTab=p->pTable; pTab; pTab=pTab->pNext){ - if( 0==sqlite3_stricmp(z3, pTab->zName) ) break; - } - if( pTab ){ - IdxWrite *pWrite; - for(pWrite=p->pWrite; pWrite; pWrite=pWrite->pNext){ - if( pWrite->pTab==pTab && pWrite->eOp==eOp ) break; - } - if( pWrite==0 ){ - pWrite = idxMalloc(&rc, sizeof(IdxWrite)); - if( rc==SQLITE_OK ){ - pWrite->pTab = pTab; - pWrite->eOp = eOp; - pWrite->pNext = p->pWrite; - p->pWrite = pWrite; - } - } - } - } - } - return rc; -} - -static int idxProcessOneTrigger( - sqlite3expert *p, - IdxWrite *pWrite, - char **pzErr -){ - static const char *zInt = UNIQUE_TABLE_NAME; - static const char *zDrop = "DROP TABLE " UNIQUE_TABLE_NAME; - IdxTable *pTab = pWrite->pTab; - const char *zTab = pTab->zName; - const char *zSql = - "SELECT 'CREATE TEMP' || substr(sql, 7) FROM sqlite_schema " - "WHERE tbl_name = %Q AND type IN ('table', 'trigger') " - "ORDER BY type;"; - sqlite3_stmt *pSelect = 0; - int rc = SQLITE_OK; - char *zWrite = 0; - - /* Create the table and its triggers in the temp schema */ - rc = idxPrintfPrepareStmt(p->db, &pSelect, pzErr, zSql, zTab, zTab); - while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pSelect) ){ - const char *zCreate = (const char*)sqlite3_column_text(pSelect, 0); - rc = sqlite3_exec(p->dbv, zCreate, 0, 0, pzErr); - } - idxFinalize(&rc, pSelect); - - /* Rename the table in the temp schema to zInt */ - if( rc==SQLITE_OK ){ - char *z = sqlite3_mprintf("ALTER TABLE temp.%Q RENAME TO %Q", zTab, zInt); - if( z==0 ){ - rc = SQLITE_NOMEM; - }else{ - rc = sqlite3_exec(p->dbv, z, 0, 0, pzErr); - sqlite3_free(z); - } - } - - switch( pWrite->eOp ){ - case SQLITE_INSERT: { - int i; - zWrite = idxAppendText(&rc, zWrite, "INSERT INTO %Q VALUES(", zInt); - for(i=0; inCol; i++){ - zWrite = idxAppendText(&rc, zWrite, "%s?", i==0 ? "" : ", "); - } - zWrite = idxAppendText(&rc, zWrite, ")"); - break; - } - case SQLITE_UPDATE: { - int i; - zWrite = idxAppendText(&rc, zWrite, "UPDATE %Q SET ", zInt); - for(i=0; inCol; i++){ - zWrite = idxAppendText(&rc, zWrite, "%s%Q=?", i==0 ? "" : ", ", - pTab->aCol[i].zName - ); - } - break; - } - default: { - assert( pWrite->eOp==SQLITE_DELETE ); - if( rc==SQLITE_OK ){ - zWrite = sqlite3_mprintf("DELETE FROM %Q", zInt); - if( zWrite==0 ) rc = SQLITE_NOMEM; - } - } - } - - if( rc==SQLITE_OK ){ - sqlite3_stmt *pX = 0; - rc = sqlite3_prepare_v2(p->dbv, zWrite, -1, &pX, 0); - idxFinalize(&rc, pX); - if( rc!=SQLITE_OK ){ - idxDatabaseError(p->dbv, pzErr); - } - } - sqlite3_free(zWrite); - - if( rc==SQLITE_OK ){ - rc = sqlite3_exec(p->dbv, zDrop, 0, 0, pzErr); - } - - return rc; -} - -static int idxProcessTriggers(sqlite3expert *p, char **pzErr){ - int rc = SQLITE_OK; - IdxWrite *pEnd = 0; - IdxWrite *pFirst = p->pWrite; - - while( rc==SQLITE_OK && pFirst!=pEnd ){ - IdxWrite *pIter; - for(pIter=pFirst; rc==SQLITE_OK && pIter!=pEnd; pIter=pIter->pNext){ - rc = idxProcessOneTrigger(p, pIter, pzErr); - } - pEnd = pFirst; - pFirst = p->pWrite; - } - - return rc; -} - - -static int idxCreateVtabSchema(sqlite3expert *p, char **pzErrmsg){ - int rc = idxRegisterVtab(p); - sqlite3_stmt *pSchema = 0; - - /* For each table in the main db schema: - ** - ** 1) Add an entry to the p->pTable list, and - ** 2) Create the equivalent virtual table in dbv. - */ - rc = idxPrepareStmt(p->db, &pSchema, pzErrmsg, - "SELECT type, name, sql, 1 FROM sqlite_schema " - "WHERE type IN ('table','view') AND name NOT LIKE 'sqlite_%%' " - " UNION ALL " - "SELECT type, name, sql, 2 FROM sqlite_schema " - "WHERE type = 'trigger'" - " AND tbl_name IN(SELECT name FROM sqlite_schema WHERE type = 'view') " - "ORDER BY 4, 1" - ); - while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pSchema) ){ - const char *zType = (const char*)sqlite3_column_text(pSchema, 0); - const char *zName = (const char*)sqlite3_column_text(pSchema, 1); - const char *zSql = (const char*)sqlite3_column_text(pSchema, 2); - - if( zType[0]=='v' || zType[1]=='r' ){ - rc = sqlite3_exec(p->dbv, zSql, 0, 0, pzErrmsg); - }else{ - IdxTable *pTab; - rc = idxGetTableInfo(p->db, zName, &pTab, pzErrmsg); - if( rc==SQLITE_OK ){ - int i; - char *zInner = 0; - char *zOuter = 0; - pTab->pNext = p->pTable; - p->pTable = pTab; - - /* The statement the vtab will pass to sqlite3_declare_vtab() */ - zInner = idxAppendText(&rc, 0, "CREATE TABLE x("); - for(i=0; inCol; i++){ - zInner = idxAppendText(&rc, zInner, "%s%Q COLLATE %s", - (i==0 ? "" : ", "), pTab->aCol[i].zName, pTab->aCol[i].zColl - ); - } - zInner = idxAppendText(&rc, zInner, ")"); - - /* The CVT statement to create the vtab */ - zOuter = idxAppendText(&rc, 0, - "CREATE VIRTUAL TABLE %Q USING expert(%Q)", zName, zInner - ); - if( rc==SQLITE_OK ){ - rc = sqlite3_exec(p->dbv, zOuter, 0, 0, pzErrmsg); - } - sqlite3_free(zInner); - sqlite3_free(zOuter); - } - } - } - idxFinalize(&rc, pSchema); - return rc; -} - -struct IdxSampleCtx { - int iTarget; - double target; /* Target nRet/nRow value */ - double nRow; /* Number of rows seen */ - double nRet; /* Number of rows returned */ -}; - -static void idxSampleFunc( - sqlite3_context *pCtx, - int argc, - sqlite3_value **argv -){ - struct IdxSampleCtx *p = (struct IdxSampleCtx*)sqlite3_user_data(pCtx); - int bRet; - - (void)argv; - assert( argc==0 ); - if( p->nRow==0.0 ){ - bRet = 1; - }else{ - bRet = (p->nRet / p->nRow) <= p->target; - if( bRet==0 ){ - unsigned short rnd; - sqlite3_randomness(2, (void*)&rnd); - bRet = ((int)rnd % 100) <= p->iTarget; - } - } - - sqlite3_result_int(pCtx, bRet); - p->nRow += 1.0; - p->nRet += (double)bRet; -} - -struct IdxRemCtx { - int nSlot; - struct IdxRemSlot { - int eType; /* SQLITE_NULL, INTEGER, REAL, TEXT, BLOB */ - i64 iVal; /* SQLITE_INTEGER value */ - double rVal; /* SQLITE_FLOAT value */ - int nByte; /* Bytes of space allocated at z */ - int n; /* Size of buffer z */ - char *z; /* SQLITE_TEXT/BLOB value */ - } aSlot[1]; -}; - -/* -** Implementation of scalar function rem(). -*/ -static void idxRemFunc( - sqlite3_context *pCtx, - int argc, - sqlite3_value **argv -){ - struct IdxRemCtx *p = (struct IdxRemCtx*)sqlite3_user_data(pCtx); - struct IdxRemSlot *pSlot; - int iSlot; - assert( argc==2 ); - - iSlot = sqlite3_value_int(argv[0]); - assert( iSlot<=p->nSlot ); - pSlot = &p->aSlot[iSlot]; - - switch( pSlot->eType ){ - case SQLITE_NULL: - /* no-op */ - break; - - case SQLITE_INTEGER: - sqlite3_result_int64(pCtx, pSlot->iVal); - break; - - case SQLITE_FLOAT: - sqlite3_result_double(pCtx, pSlot->rVal); - break; - - case SQLITE_BLOB: - sqlite3_result_blob(pCtx, pSlot->z, pSlot->n, SQLITE_TRANSIENT); - break; - - case SQLITE_TEXT: - sqlite3_result_text(pCtx, pSlot->z, pSlot->n, SQLITE_TRANSIENT); - break; - } - - pSlot->eType = sqlite3_value_type(argv[1]); - switch( pSlot->eType ){ - case SQLITE_NULL: - /* no-op */ - break; - - case SQLITE_INTEGER: - pSlot->iVal = sqlite3_value_int64(argv[1]); - break; - - case SQLITE_FLOAT: - pSlot->rVal = sqlite3_value_double(argv[1]); - break; - - case SQLITE_BLOB: - case SQLITE_TEXT: { - int nByte = sqlite3_value_bytes(argv[1]); - if( nByte>pSlot->nByte ){ - char *zNew = (char*)sqlite3_realloc(pSlot->z, nByte*2); - if( zNew==0 ){ - sqlite3_result_error_nomem(pCtx); - return; - } - pSlot->nByte = nByte*2; - pSlot->z = zNew; - } - pSlot->n = nByte; - if( pSlot->eType==SQLITE_BLOB ){ - memcpy(pSlot->z, sqlite3_value_blob(argv[1]), nByte); - }else{ - memcpy(pSlot->z, sqlite3_value_text(argv[1]), nByte); - } - break; - } - } -} - -static int idxLargestIndex(sqlite3 *db, int *pnMax, char **pzErr){ - int rc = SQLITE_OK; - const char *zMax = - "SELECT max(i.seqno) FROM " - " sqlite_schema AS s, " - " pragma_index_list(s.name) AS l, " - " pragma_index_info(l.name) AS i " - "WHERE s.type = 'table'"; - sqlite3_stmt *pMax = 0; - - *pnMax = 0; - rc = idxPrepareStmt(db, &pMax, pzErr, zMax); - if( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pMax) ){ - *pnMax = sqlite3_column_int(pMax, 0) + 1; - } - idxFinalize(&rc, pMax); - - return rc; -} - -static int idxPopulateOneStat1( - sqlite3expert *p, - sqlite3_stmt *pIndexXInfo, - sqlite3_stmt *pWriteStat, - const char *zTab, - const char *zIdx, - char **pzErr -){ - char *zCols = 0; - char *zOrder = 0; - char *zQuery = 0; - int nCol = 0; - int i; - sqlite3_stmt *pQuery = 0; - int *aStat = 0; - int rc = SQLITE_OK; - - assert( p->iSample>0 ); - - /* Formulate the query text */ - sqlite3_bind_text(pIndexXInfo, 1, zIdx, -1, SQLITE_STATIC); - while( SQLITE_OK==rc && SQLITE_ROW==sqlite3_step(pIndexXInfo) ){ - const char *zComma = zCols==0 ? "" : ", "; - const char *zName = (const char*)sqlite3_column_text(pIndexXInfo, 0); - const char *zColl = (const char*)sqlite3_column_text(pIndexXInfo, 1); - zCols = idxAppendText(&rc, zCols, - "%sx.%Q IS rem(%d, x.%Q) COLLATE %s", zComma, zName, nCol, zName, zColl - ); - zOrder = idxAppendText(&rc, zOrder, "%s%d", zComma, ++nCol); - } - sqlite3_reset(pIndexXInfo); - if( rc==SQLITE_OK ){ - if( p->iSample==100 ){ - zQuery = sqlite3_mprintf( - "SELECT %s FROM %Q x ORDER BY %s", zCols, zTab, zOrder - ); - }else{ - zQuery = sqlite3_mprintf( - "SELECT %s FROM temp."UNIQUE_TABLE_NAME" x ORDER BY %s", zCols, zOrder - ); - } - } - sqlite3_free(zCols); - sqlite3_free(zOrder); - - /* Formulate the query text */ - if( rc==SQLITE_OK ){ - sqlite3 *dbrem = (p->iSample==100 ? p->db : p->dbv); - rc = idxPrepareStmt(dbrem, &pQuery, pzErr, zQuery); - } - sqlite3_free(zQuery); - - if( rc==SQLITE_OK ){ - aStat = (int*)idxMalloc(&rc, sizeof(int)*(nCol+1)); - } - if( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pQuery) ){ - IdxHashEntry *pEntry; - char *zStat = 0; - for(i=0; i<=nCol; i++) aStat[i] = 1; - while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pQuery) ){ - aStat[0]++; - for(i=0; ihIdx, zIdx, STRLEN(zIdx)); - if( pEntry ){ - assert( pEntry->zVal2==0 ); - pEntry->zVal2 = zStat; - }else{ - sqlite3_free(zStat); - } - } - sqlite3_free(aStat); - idxFinalize(&rc, pQuery); - - return rc; -} - -static int idxBuildSampleTable(sqlite3expert *p, const char *zTab){ - int rc; - char *zSql; - - rc = sqlite3_exec(p->dbv,"DROP TABLE IF EXISTS temp."UNIQUE_TABLE_NAME,0,0,0); - if( rc!=SQLITE_OK ) return rc; - - zSql = sqlite3_mprintf( - "CREATE TABLE temp." UNIQUE_TABLE_NAME " AS SELECT * FROM %Q", zTab - ); - if( zSql==0 ) return SQLITE_NOMEM; - rc = sqlite3_exec(p->dbv, zSql, 0, 0, 0); - sqlite3_free(zSql); - - return rc; -} - -/* -** This function is called as part of sqlite3_expert_analyze(). Candidate -** indexes have already been created in database sqlite3expert.dbm, this -** function populates sqlite_stat1 table in the same database. -** -** The stat1 data is generated by querying the -*/ -static int idxPopulateStat1(sqlite3expert *p, char **pzErr){ - int rc = SQLITE_OK; - int nMax =0; - struct IdxRemCtx *pCtx = 0; - struct IdxSampleCtx samplectx; - int i; - i64 iPrev = -100000; - sqlite3_stmt *pAllIndex = 0; - sqlite3_stmt *pIndexXInfo = 0; - sqlite3_stmt *pWrite = 0; - - const char *zAllIndex = - "SELECT s.rowid, s.name, l.name FROM " - " sqlite_schema AS s, " - " pragma_index_list(s.name) AS l " - "WHERE s.type = 'table'"; - const char *zIndexXInfo = - "SELECT name, coll FROM pragma_index_xinfo(?) WHERE key"; - const char *zWrite = "INSERT INTO sqlite_stat1 VALUES(?, ?, ?)"; - - /* If iSample==0, no sqlite_stat1 data is required. */ - if( p->iSample==0 ) return SQLITE_OK; - - rc = idxLargestIndex(p->dbm, &nMax, pzErr); - if( nMax<=0 || rc!=SQLITE_OK ) return rc; - - rc = sqlite3_exec(p->dbm, "ANALYZE; PRAGMA writable_schema=1", 0, 0, 0); - - if( rc==SQLITE_OK ){ - int nByte = sizeof(struct IdxRemCtx) + (sizeof(struct IdxRemSlot) * nMax); - pCtx = (struct IdxRemCtx*)idxMalloc(&rc, nByte); - } - - if( rc==SQLITE_OK ){ - sqlite3 *dbrem = (p->iSample==100 ? p->db : p->dbv); - rc = sqlite3_create_function( - dbrem, "rem", 2, SQLITE_UTF8, (void*)pCtx, idxRemFunc, 0, 0 - ); - } - if( rc==SQLITE_OK ){ - rc = sqlite3_create_function( - p->db, "sample", 0, SQLITE_UTF8, (void*)&samplectx, idxSampleFunc, 0, 0 - ); - } - - if( rc==SQLITE_OK ){ - pCtx->nSlot = nMax+1; - rc = idxPrepareStmt(p->dbm, &pAllIndex, pzErr, zAllIndex); - } - if( rc==SQLITE_OK ){ - rc = idxPrepareStmt(p->dbm, &pIndexXInfo, pzErr, zIndexXInfo); - } - if( rc==SQLITE_OK ){ - rc = idxPrepareStmt(p->dbm, &pWrite, pzErr, zWrite); - } - - while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pAllIndex) ){ - i64 iRowid = sqlite3_column_int64(pAllIndex, 0); - const char *zTab = (const char*)sqlite3_column_text(pAllIndex, 1); - const char *zIdx = (const char*)sqlite3_column_text(pAllIndex, 2); - if( p->iSample<100 && iPrev!=iRowid ){ - samplectx.target = (double)p->iSample / 100.0; - samplectx.iTarget = p->iSample; - samplectx.nRow = 0.0; - samplectx.nRet = 0.0; - rc = idxBuildSampleTable(p, zTab); - if( rc!=SQLITE_OK ) break; - } - rc = idxPopulateOneStat1(p, pIndexXInfo, pWrite, zTab, zIdx, pzErr); - iPrev = iRowid; - } - if( rc==SQLITE_OK && p->iSample<100 ){ - rc = sqlite3_exec(p->dbv, - "DROP TABLE IF EXISTS temp." UNIQUE_TABLE_NAME, 0,0,0 - ); - } - - idxFinalize(&rc, pAllIndex); - idxFinalize(&rc, pIndexXInfo); - idxFinalize(&rc, pWrite); - - for(i=0; inSlot; i++){ - sqlite3_free(pCtx->aSlot[i].z); - } - sqlite3_free(pCtx); - - if( rc==SQLITE_OK ){ - rc = sqlite3_exec(p->dbm, "ANALYZE sqlite_schema", 0, 0, 0); - } - - sqlite3_exec(p->db, "DROP TABLE IF EXISTS temp."UNIQUE_TABLE_NAME,0,0,0); - return rc; -} - -/* -** Allocate a new sqlite3expert object. -*/ -sqlite3expert *sqlite3_expert_new(sqlite3 *db, char **pzErrmsg){ - int rc = SQLITE_OK; - sqlite3expert *pNew; - - pNew = (sqlite3expert*)idxMalloc(&rc, sizeof(sqlite3expert)); - - /* Open two in-memory databases to work with. The "vtab database" (dbv) - ** will contain a virtual table corresponding to each real table in - ** the user database schema, and a copy of each view. It is used to - ** collect information regarding the WHERE, ORDER BY and other clauses - ** of the user's query. - */ - if( rc==SQLITE_OK ){ - pNew->db = db; - pNew->iSample = 100; - rc = sqlite3_open(":memory:", &pNew->dbv); - } - if( rc==SQLITE_OK ){ - rc = sqlite3_open(":memory:", &pNew->dbm); - if( rc==SQLITE_OK ){ - sqlite3_db_config(pNew->dbm, SQLITE_DBCONFIG_TRIGGER_EQP, 1, (int*)0); - } - } - - - /* Copy the entire schema of database [db] into [dbm]. */ - if( rc==SQLITE_OK ){ - sqlite3_stmt *pSql; - rc = idxPrintfPrepareStmt(pNew->db, &pSql, pzErrmsg, - "SELECT sql FROM sqlite_schema WHERE name NOT LIKE 'sqlite_%%'" - " AND sql NOT LIKE 'CREATE VIRTUAL %%'" - ); - while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pSql) ){ - const char *zSql = (const char*)sqlite3_column_text(pSql, 0); - rc = sqlite3_exec(pNew->dbm, zSql, 0, 0, pzErrmsg); - } - idxFinalize(&rc, pSql); - } - - /* Create the vtab schema */ - if( rc==SQLITE_OK ){ - rc = idxCreateVtabSchema(pNew, pzErrmsg); - } - - /* Register the auth callback with dbv */ - if( rc==SQLITE_OK ){ - sqlite3_set_authorizer(pNew->dbv, idxAuthCallback, (void*)pNew); - } - - /* If an error has occurred, free the new object and reutrn NULL. Otherwise, - ** return the new sqlite3expert handle. */ - if( rc!=SQLITE_OK ){ - sqlite3_expert_destroy(pNew); - pNew = 0; - } - return pNew; -} - -/* -** Configure an sqlite3expert object. -*/ -int sqlite3_expert_config(sqlite3expert *p, int op, ...){ - int rc = SQLITE_OK; - va_list ap; - va_start(ap, op); - switch( op ){ - case EXPERT_CONFIG_SAMPLE: { - int iVal = va_arg(ap, int); - if( iVal<0 ) iVal = 0; - if( iVal>100 ) iVal = 100; - p->iSample = iVal; - break; - } - default: - rc = SQLITE_NOTFOUND; - break; - } - - va_end(ap); - return rc; -} - -/* -** Add an SQL statement to the analysis. -*/ -int sqlite3_expert_sql( - sqlite3expert *p, /* From sqlite3_expert_new() */ - const char *zSql, /* SQL statement to add */ - char **pzErr /* OUT: Error message (if any) */ -){ - IdxScan *pScanOrig = p->pScan; - IdxStatement *pStmtOrig = p->pStatement; - int rc = SQLITE_OK; - const char *zStmt = zSql; - - if( p->bRun ) return SQLITE_MISUSE; - - while( rc==SQLITE_OK && zStmt && zStmt[0] ){ - sqlite3_stmt *pStmt = 0; - rc = sqlite3_prepare_v2(p->dbv, zStmt, -1, &pStmt, &zStmt); - if( rc==SQLITE_OK ){ - if( pStmt ){ - IdxStatement *pNew; - const char *z = sqlite3_sql(pStmt); - int n = STRLEN(z); - pNew = (IdxStatement*)idxMalloc(&rc, sizeof(IdxStatement) + n+1); - if( rc==SQLITE_OK ){ - pNew->zSql = (char*)&pNew[1]; - memcpy(pNew->zSql, z, n+1); - pNew->pNext = p->pStatement; - if( p->pStatement ) pNew->iId = p->pStatement->iId+1; - p->pStatement = pNew; - } - sqlite3_finalize(pStmt); - } - }else{ - idxDatabaseError(p->dbv, pzErr); - } - } - - if( rc!=SQLITE_OK ){ - idxScanFree(p->pScan, pScanOrig); - idxStatementFree(p->pStatement, pStmtOrig); - p->pScan = pScanOrig; - p->pStatement = pStmtOrig; - } - - return rc; -} - -int sqlite3_expert_analyze(sqlite3expert *p, char **pzErr){ - int rc; - IdxHashEntry *pEntry; - - /* Do trigger processing to collect any extra IdxScan structures */ - rc = idxProcessTriggers(p, pzErr); - - /* Create candidate indexes within the in-memory database file */ - if( rc==SQLITE_OK ){ - rc = idxCreateCandidates(p); - } - - /* Generate the stat1 data */ - if( rc==SQLITE_OK ){ - rc = idxPopulateStat1(p, pzErr); - } - - /* Formulate the EXPERT_REPORT_CANDIDATES text */ - for(pEntry=p->hIdx.pFirst; pEntry; pEntry=pEntry->pNext){ - p->zCandidates = idxAppendText(&rc, p->zCandidates, - "%s;%s%s\n", pEntry->zVal, - pEntry->zVal2 ? " -- stat1: " : "", pEntry->zVal2 - ); - } - - /* Figure out which of the candidate indexes are preferred by the query - ** planner and report the results to the user. */ - if( rc==SQLITE_OK ){ - rc = idxFindIndexes(p, pzErr); - } - - if( rc==SQLITE_OK ){ - p->bRun = 1; - } - return rc; -} - -/* -** Return the total number of statements that have been added to this -** sqlite3expert using sqlite3_expert_sql(). -*/ -int sqlite3_expert_count(sqlite3expert *p){ - int nRet = 0; - if( p->pStatement ) nRet = p->pStatement->iId+1; - return nRet; -} - -/* -** Return a component of the report. -*/ -const char *sqlite3_expert_report(sqlite3expert *p, int iStmt, int eReport){ - const char *zRet = 0; - IdxStatement *pStmt; - - if( p->bRun==0 ) return 0; - for(pStmt=p->pStatement; pStmt && pStmt->iId!=iStmt; pStmt=pStmt->pNext); - switch( eReport ){ - case EXPERT_REPORT_SQL: - if( pStmt ) zRet = pStmt->zSql; - break; - case EXPERT_REPORT_INDEXES: - if( pStmt ) zRet = pStmt->zIdx; - break; - case EXPERT_REPORT_PLAN: - if( pStmt ) zRet = pStmt->zEQP; - break; - case EXPERT_REPORT_CANDIDATES: - zRet = p->zCandidates; - break; - } - return zRet; -} - -/* -** Free an sqlite3expert object. -*/ -void sqlite3_expert_destroy(sqlite3expert *p){ - if( p ){ - sqlite3_close(p->dbm); - sqlite3_close(p->dbv); - idxScanFree(p->pScan, 0); - idxStatementFree(p->pStatement, 0); - idxTableFree(p->pTable); - idxWriteFree(p->pWrite); - idxHashClear(&p->hIdx); - sqlite3_free(p->zCandidates); - sqlite3_free(p); - } -} - -#endif /* ifndef SQLITE_OMIT_VIRTUALTABLE */ - -/************************* End ../ext/expert/sqlite3expert.c ********************/ - -#if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_ENABLE_DBPAGE_VTAB) -/************************* Begin ../ext/misc/dbdata.c ******************/ -/* -** 2019-04-17 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -****************************************************************************** -** -** This file contains an implementation of two eponymous virtual tables, -** "sqlite_dbdata" and "sqlite_dbptr". Both modules require that the -** "sqlite_dbpage" eponymous virtual table be available. -** -** SQLITE_DBDATA: -** sqlite_dbdata is used to extract data directly from a database b-tree -** page and its associated overflow pages, bypassing the b-tree layer. -** The table schema is equivalent to: -** -** CREATE TABLE sqlite_dbdata( -** pgno INTEGER, -** cell INTEGER, -** field INTEGER, -** value ANY, -** schema TEXT HIDDEN -** ); -** -** IMPORTANT: THE VIRTUAL TABLE SCHEMA ABOVE IS SUBJECT TO CHANGE. IN THE -** FUTURE NEW NON-HIDDEN COLUMNS MAY BE ADDED BETWEEN "value" AND -** "schema". -** -** Each page of the database is inspected. If it cannot be interpreted as -** a b-tree page, or if it is a b-tree page containing 0 entries, the -** sqlite_dbdata table contains no rows for that page. Otherwise, the -** table contains one row for each field in the record associated with -** each cell on the page. For intkey b-trees, the key value is stored in -** field -1. -** -** For example, for the database: -** -** CREATE TABLE t1(a, b); -- root page is page 2 -** INSERT INTO t1(rowid, a, b) VALUES(5, 'v', 'five'); -** INSERT INTO t1(rowid, a, b) VALUES(10, 'x', 'ten'); -** -** the sqlite_dbdata table contains, as well as from entries related to -** page 1, content equivalent to: -** -** INSERT INTO sqlite_dbdata(pgno, cell, field, value) VALUES -** (2, 0, -1, 5 ), -** (2, 0, 0, 'v' ), -** (2, 0, 1, 'five'), -** (2, 1, -1, 10 ), -** (2, 1, 0, 'x' ), -** (2, 1, 1, 'ten' ); -** -** If database corruption is encountered, this module does not report an -** error. Instead, it attempts to extract as much data as possible and -** ignores the corruption. -** -** SQLITE_DBPTR: -** The sqlite_dbptr table has the following schema: -** -** CREATE TABLE sqlite_dbptr( -** pgno INTEGER, -** child INTEGER, -** schema TEXT HIDDEN -** ); -** -** It contains one entry for each b-tree pointer between a parent and -** child page in the database. -*/ -#if !defined(SQLITEINT_H) -/* #include "sqlite3ext.h" */ - -/* typedef unsigned char u8; */ - -#endif -SQLITE_EXTENSION_INIT1 -#include -#include - -#define DBDATA_PADDING_BYTES 100 - -typedef struct DbdataTable DbdataTable; -typedef struct DbdataCursor DbdataCursor; - -/* Cursor object */ -struct DbdataCursor { - sqlite3_vtab_cursor base; /* Base class. Must be first */ - sqlite3_stmt *pStmt; /* For fetching database pages */ - - int iPgno; /* Current page number */ - u8 *aPage; /* Buffer containing page */ - int nPage; /* Size of aPage[] in bytes */ - int nCell; /* Number of cells on aPage[] */ - int iCell; /* Current cell number */ - int bOnePage; /* True to stop after one page */ - int szDb; - sqlite3_int64 iRowid; - - /* Only for the sqlite_dbdata table */ - u8 *pRec; /* Buffer containing current record */ - int nRec; /* Size of pRec[] in bytes */ - int nHdr; /* Size of header in bytes */ - int iField; /* Current field number */ - u8 *pHdrPtr; - u8 *pPtr; - - sqlite3_int64 iIntkey; /* Integer key value */ -}; - -/* Table object */ -struct DbdataTable { - sqlite3_vtab base; /* Base class. Must be first */ - sqlite3 *db; /* The database connection */ - sqlite3_stmt *pStmt; /* For fetching database pages */ - int bPtr; /* True for sqlite3_dbptr table */ -}; - -/* Column and schema definitions for sqlite_dbdata */ -#define DBDATA_COLUMN_PGNO 0 -#define DBDATA_COLUMN_CELL 1 -#define DBDATA_COLUMN_FIELD 2 -#define DBDATA_COLUMN_VALUE 3 -#define DBDATA_COLUMN_SCHEMA 4 -#define DBDATA_SCHEMA \ - "CREATE TABLE x(" \ - " pgno INTEGER," \ - " cell INTEGER," \ - " field INTEGER," \ - " value ANY," \ - " schema TEXT HIDDEN" \ - ")" - -/* Column and schema definitions for sqlite_dbptr */ -#define DBPTR_COLUMN_PGNO 0 -#define DBPTR_COLUMN_CHILD 1 -#define DBPTR_COLUMN_SCHEMA 2 -#define DBPTR_SCHEMA \ - "CREATE TABLE x(" \ - " pgno INTEGER," \ - " child INTEGER," \ - " schema TEXT HIDDEN" \ - ")" - -/* -** Connect to an sqlite_dbdata (pAux==0) or sqlite_dbptr (pAux!=0) virtual -** table. -*/ -static int dbdataConnect( - sqlite3 *db, - void *pAux, - int argc, const char *const*argv, - sqlite3_vtab **ppVtab, - char **pzErr -){ - DbdataTable *pTab = 0; - int rc = sqlite3_declare_vtab(db, pAux ? DBPTR_SCHEMA : DBDATA_SCHEMA); - - if( rc==SQLITE_OK ){ - pTab = (DbdataTable*)sqlite3_malloc64(sizeof(DbdataTable)); - if( pTab==0 ){ - rc = SQLITE_NOMEM; - }else{ - memset(pTab, 0, sizeof(DbdataTable)); - pTab->db = db; - pTab->bPtr = (pAux!=0); - } - } - - *ppVtab = (sqlite3_vtab*)pTab; - return rc; -} - -/* -** Disconnect from or destroy a sqlite_dbdata or sqlite_dbptr virtual table. -*/ -static int dbdataDisconnect(sqlite3_vtab *pVtab){ - DbdataTable *pTab = (DbdataTable*)pVtab; - if( pTab ){ - sqlite3_finalize(pTab->pStmt); - sqlite3_free(pVtab); - } - return SQLITE_OK; -} - -/* -** This function interprets two types of constraints: -** -** schema=? -** pgno=? -** -** If neither are present, idxNum is set to 0. If schema=? is present, -** the 0x01 bit in idxNum is set. If pgno=? is present, the 0x02 bit -** in idxNum is set. -** -** If both parameters are present, schema is in position 0 and pgno in -** position 1. -*/ -static int dbdataBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdx){ - DbdataTable *pTab = (DbdataTable*)tab; - int i; - int iSchema = -1; - int iPgno = -1; - int colSchema = (pTab->bPtr ? DBPTR_COLUMN_SCHEMA : DBDATA_COLUMN_SCHEMA); - - for(i=0; inConstraint; i++){ - struct sqlite3_index_constraint *p = &pIdx->aConstraint[i]; - if( p->op==SQLITE_INDEX_CONSTRAINT_EQ ){ - if( p->iColumn==colSchema ){ - if( p->usable==0 ) return SQLITE_CONSTRAINT; - iSchema = i; - } - if( p->iColumn==DBDATA_COLUMN_PGNO && p->usable ){ - iPgno = i; - } - } - } - - if( iSchema>=0 ){ - pIdx->aConstraintUsage[iSchema].argvIndex = 1; - pIdx->aConstraintUsage[iSchema].omit = 1; - } - if( iPgno>=0 ){ - pIdx->aConstraintUsage[iPgno].argvIndex = 1 + (iSchema>=0); - pIdx->aConstraintUsage[iPgno].omit = 1; - pIdx->estimatedCost = 100; - pIdx->estimatedRows = 50; - - if( pTab->bPtr==0 && pIdx->nOrderBy && pIdx->aOrderBy[0].desc==0 ){ - int iCol = pIdx->aOrderBy[0].iColumn; - if( pIdx->nOrderBy==1 ){ - pIdx->orderByConsumed = (iCol==0 || iCol==1); - }else if( pIdx->nOrderBy==2 && pIdx->aOrderBy[1].desc==0 && iCol==0 ){ - pIdx->orderByConsumed = (pIdx->aOrderBy[1].iColumn==1); - } - } - - }else{ - pIdx->estimatedCost = 100000000; - pIdx->estimatedRows = 1000000000; - } - pIdx->idxNum = (iSchema>=0 ? 0x01 : 0x00) | (iPgno>=0 ? 0x02 : 0x00); - return SQLITE_OK; -} - -/* -** Open a new sqlite_dbdata or sqlite_dbptr cursor. -*/ -static int dbdataOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){ - DbdataCursor *pCsr; - - pCsr = (DbdataCursor*)sqlite3_malloc64(sizeof(DbdataCursor)); - if( pCsr==0 ){ - return SQLITE_NOMEM; - }else{ - memset(pCsr, 0, sizeof(DbdataCursor)); - pCsr->base.pVtab = pVTab; - } - - *ppCursor = (sqlite3_vtab_cursor *)pCsr; - return SQLITE_OK; -} - -/* -** Restore a cursor object to the state it was in when first allocated -** by dbdataOpen(). -*/ -static void dbdataResetCursor(DbdataCursor *pCsr){ - DbdataTable *pTab = (DbdataTable*)(pCsr->base.pVtab); - if( pTab->pStmt==0 ){ - pTab->pStmt = pCsr->pStmt; - }else{ - sqlite3_finalize(pCsr->pStmt); - } - pCsr->pStmt = 0; - pCsr->iPgno = 1; - pCsr->iCell = 0; - pCsr->iField = 0; - pCsr->bOnePage = 0; - sqlite3_free(pCsr->aPage); - sqlite3_free(pCsr->pRec); - pCsr->pRec = 0; - pCsr->aPage = 0; -} - -/* -** Close an sqlite_dbdata or sqlite_dbptr cursor. -*/ -static int dbdataClose(sqlite3_vtab_cursor *pCursor){ - DbdataCursor *pCsr = (DbdataCursor*)pCursor; - dbdataResetCursor(pCsr); - sqlite3_free(pCsr); - return SQLITE_OK; -} - -/* -** Utility methods to decode 16 and 32-bit big-endian unsigned integers. -*/ -static unsigned int get_uint16(unsigned char *a){ - return (a[0]<<8)|a[1]; -} -static unsigned int get_uint32(unsigned char *a){ - return ((unsigned int)a[0]<<24) - | ((unsigned int)a[1]<<16) - | ((unsigned int)a[2]<<8) - | ((unsigned int)a[3]); -} - -/* -** Load page pgno from the database via the sqlite_dbpage virtual table. -** If successful, set (*ppPage) to point to a buffer containing the page -** data, (*pnPage) to the size of that buffer in bytes and return -** SQLITE_OK. In this case it is the responsibility of the caller to -** eventually free the buffer using sqlite3_free(). -** -** Or, if an error occurs, set both (*ppPage) and (*pnPage) to 0 and -** return an SQLite error code. -*/ -static int dbdataLoadPage( - DbdataCursor *pCsr, /* Cursor object */ - unsigned int pgno, /* Page number of page to load */ - u8 **ppPage, /* OUT: pointer to page buffer */ - int *pnPage /* OUT: Size of (*ppPage) in bytes */ -){ - int rc2; - int rc = SQLITE_OK; - sqlite3_stmt *pStmt = pCsr->pStmt; - - *ppPage = 0; - *pnPage = 0; - sqlite3_bind_int64(pStmt, 2, pgno); - if( SQLITE_ROW==sqlite3_step(pStmt) ){ - int nCopy = sqlite3_column_bytes(pStmt, 0); - if( nCopy>0 ){ - u8 *pPage; - pPage = (u8*)sqlite3_malloc64(nCopy + DBDATA_PADDING_BYTES); - if( pPage==0 ){ - rc = SQLITE_NOMEM; - }else{ - const u8 *pCopy = sqlite3_column_blob(pStmt, 0); - memcpy(pPage, pCopy, nCopy); - memset(&pPage[nCopy], 0, DBDATA_PADDING_BYTES); - } - *ppPage = pPage; - *pnPage = nCopy; - } - } - rc2 = sqlite3_reset(pStmt); - if( rc==SQLITE_OK ) rc = rc2; - - return rc; -} - -/* -** Read a varint. Put the value in *pVal and return the number of bytes. -*/ -static int dbdataGetVarint(const u8 *z, sqlite3_int64 *pVal){ - sqlite3_int64 v = 0; - int i; - for(i=0; i<8; i++){ - v = (v<<7) + (z[i]&0x7f); - if( (z[i]&0x80)==0 ){ *pVal = v; return i+1; } - } - v = (v<<8) + (z[i]&0xff); - *pVal = v; - return 9; -} - -/* -** Return the number of bytes of space used by an SQLite value of type -** eType. -*/ -static int dbdataValueBytes(int eType){ - switch( eType ){ - case 0: case 8: case 9: - case 10: case 11: - return 0; - case 1: - return 1; - case 2: - return 2; - case 3: - return 3; - case 4: - return 4; - case 5: - return 6; - case 6: - case 7: - return 8; - default: - if( eType>0 ){ - return ((eType-12) / 2); - } - return 0; - } -} - -/* -** Load a value of type eType from buffer pData and use it to set the -** result of context object pCtx. -*/ -static void dbdataValue( - sqlite3_context *pCtx, - int eType, - u8 *pData, - int nData -){ - if( eType>=0 && dbdataValueBytes(eType)<=nData ){ - switch( eType ){ - case 0: - case 10: - case 11: - sqlite3_result_null(pCtx); - break; - - case 8: - sqlite3_result_int(pCtx, 0); - break; - case 9: - sqlite3_result_int(pCtx, 1); - break; - - case 1: case 2: case 3: case 4: case 5: case 6: case 7: { - sqlite3_uint64 v = (signed char)pData[0]; - pData++; - switch( eType ){ - case 7: - case 6: v = (v<<16) + (pData[0]<<8) + pData[1]; pData += 2; - case 5: v = (v<<16) + (pData[0]<<8) + pData[1]; pData += 2; - case 4: v = (v<<8) + pData[0]; pData++; - case 3: v = (v<<8) + pData[0]; pData++; - case 2: v = (v<<8) + pData[0]; pData++; - } - - if( eType==7 ){ - double r; - memcpy(&r, &v, sizeof(r)); - sqlite3_result_double(pCtx, r); - }else{ - sqlite3_result_int64(pCtx, (sqlite3_int64)v); - } - break; - } - - default: { - int n = ((eType-12) / 2); - if( eType % 2 ){ - sqlite3_result_text(pCtx, (const char*)pData, n, SQLITE_TRANSIENT); - }else{ - sqlite3_result_blob(pCtx, pData, n, SQLITE_TRANSIENT); - } - } - } - } -} - -/* -** Move an sqlite_dbdata or sqlite_dbptr cursor to the next entry. -*/ -static int dbdataNext(sqlite3_vtab_cursor *pCursor){ - DbdataCursor *pCsr = (DbdataCursor*)pCursor; - DbdataTable *pTab = (DbdataTable*)pCursor->pVtab; - - pCsr->iRowid++; - while( 1 ){ - int rc; - int iOff = (pCsr->iPgno==1 ? 100 : 0); - int bNextPage = 0; - - if( pCsr->aPage==0 ){ - while( 1 ){ - if( pCsr->bOnePage==0 && pCsr->iPgno>pCsr->szDb ) return SQLITE_OK; - rc = dbdataLoadPage(pCsr, pCsr->iPgno, &pCsr->aPage, &pCsr->nPage); - if( rc!=SQLITE_OK ) return rc; - if( pCsr->aPage ) break; - pCsr->iPgno++; - } - pCsr->iCell = pTab->bPtr ? -2 : 0; - pCsr->nCell = get_uint16(&pCsr->aPage[iOff+3]); - } - - if( pTab->bPtr ){ - if( pCsr->aPage[iOff]!=0x02 && pCsr->aPage[iOff]!=0x05 ){ - pCsr->iCell = pCsr->nCell; - } - pCsr->iCell++; - if( pCsr->iCell>=pCsr->nCell ){ - sqlite3_free(pCsr->aPage); - pCsr->aPage = 0; - if( pCsr->bOnePage ) return SQLITE_OK; - pCsr->iPgno++; - }else{ - return SQLITE_OK; - } - }else{ - /* If there is no record loaded, load it now. */ - if( pCsr->pRec==0 ){ - int bHasRowid = 0; - int nPointer = 0; - sqlite3_int64 nPayload = 0; - sqlite3_int64 nHdr = 0; - int iHdr; - int U, X; - int nLocal; - - switch( pCsr->aPage[iOff] ){ - case 0x02: - nPointer = 4; - break; - case 0x0a: - break; - case 0x0d: - bHasRowid = 1; - break; - default: - /* This is not a b-tree page with records on it. Continue. */ - pCsr->iCell = pCsr->nCell; - break; - } - - if( pCsr->iCell>=pCsr->nCell ){ - bNextPage = 1; - }else{ - - iOff += 8 + nPointer + pCsr->iCell*2; - if( iOff>pCsr->nPage ){ - bNextPage = 1; - }else{ - iOff = get_uint16(&pCsr->aPage[iOff]); - } - - /* For an interior node cell, skip past the child-page number */ - iOff += nPointer; - - /* Load the "byte of payload including overflow" field */ - if( bNextPage || iOff>pCsr->nPage ){ - bNextPage = 1; - }else{ - iOff += dbdataGetVarint(&pCsr->aPage[iOff], &nPayload); - } - - /* If this is a leaf intkey cell, load the rowid */ - if( bHasRowid && !bNextPage && iOffnPage ){ - iOff += dbdataGetVarint(&pCsr->aPage[iOff], &pCsr->iIntkey); - } - - /* Figure out how much data to read from the local page */ - U = pCsr->nPage; - if( bHasRowid ){ - X = U-35; - }else{ - X = ((U-12)*64/255)-23; - } - if( nPayload<=X ){ - nLocal = nPayload; - }else{ - int M, K; - M = ((U-12)*32/255)-23; - K = M+((nPayload-M)%(U-4)); - if( K<=X ){ - nLocal = K; - }else{ - nLocal = M; - } - } - - if( bNextPage || nLocal+iOff>pCsr->nPage ){ - bNextPage = 1; - }else{ - - /* Allocate space for payload. And a bit more to catch small buffer - ** overruns caused by attempting to read a varint or similar from - ** near the end of a corrupt record. */ - pCsr->pRec = (u8*)sqlite3_malloc64(nPayload+DBDATA_PADDING_BYTES); - if( pCsr->pRec==0 ) return SQLITE_NOMEM; - memset(pCsr->pRec, 0, nPayload+DBDATA_PADDING_BYTES); - pCsr->nRec = nPayload; - - /* Load the nLocal bytes of payload */ - memcpy(pCsr->pRec, &pCsr->aPage[iOff], nLocal); - iOff += nLocal; - - /* Load content from overflow pages */ - if( nPayload>nLocal ){ - sqlite3_int64 nRem = nPayload - nLocal; - unsigned int pgnoOvfl = get_uint32(&pCsr->aPage[iOff]); - while( nRem>0 ){ - u8 *aOvfl = 0; - int nOvfl = 0; - int nCopy; - rc = dbdataLoadPage(pCsr, pgnoOvfl, &aOvfl, &nOvfl); - assert( rc!=SQLITE_OK || aOvfl==0 || nOvfl==pCsr->nPage ); - if( rc!=SQLITE_OK ) return rc; - if( aOvfl==0 ) break; - - nCopy = U-4; - if( nCopy>nRem ) nCopy = nRem; - memcpy(&pCsr->pRec[nPayload-nRem], &aOvfl[4], nCopy); - nRem -= nCopy; - - pgnoOvfl = get_uint32(aOvfl); - sqlite3_free(aOvfl); - } - } - - iHdr = dbdataGetVarint(pCsr->pRec, &nHdr); - pCsr->nHdr = nHdr; - pCsr->pHdrPtr = &pCsr->pRec[iHdr]; - pCsr->pPtr = &pCsr->pRec[pCsr->nHdr]; - pCsr->iField = (bHasRowid ? -1 : 0); - } - } - }else{ - pCsr->iField++; - if( pCsr->iField>0 ){ - sqlite3_int64 iType; - if( pCsr->pHdrPtr>&pCsr->pRec[pCsr->nRec] ){ - bNextPage = 1; - }else{ - pCsr->pHdrPtr += dbdataGetVarint(pCsr->pHdrPtr, &iType); - pCsr->pPtr += dbdataValueBytes(iType); - } - } - } - - if( bNextPage ){ - sqlite3_free(pCsr->aPage); - sqlite3_free(pCsr->pRec); - pCsr->aPage = 0; - pCsr->pRec = 0; - if( pCsr->bOnePage ) return SQLITE_OK; - pCsr->iPgno++; - }else{ - if( pCsr->iField<0 || pCsr->pHdrPtr<&pCsr->pRec[pCsr->nHdr] ){ - return SQLITE_OK; - } - - /* Advance to the next cell. The next iteration of the loop will load - ** the record and so on. */ - sqlite3_free(pCsr->pRec); - pCsr->pRec = 0; - pCsr->iCell++; - } - } - } - - assert( !"can't get here" ); - return SQLITE_OK; -} - -/* -** Return true if the cursor is at EOF. -*/ -static int dbdataEof(sqlite3_vtab_cursor *pCursor){ - DbdataCursor *pCsr = (DbdataCursor*)pCursor; - return pCsr->aPage==0; -} - -/* -** Determine the size in pages of database zSchema (where zSchema is -** "main", "temp" or the name of an attached database) and set -** pCsr->szDb accordingly. If successful, return SQLITE_OK. Otherwise, -** an SQLite error code. -*/ -static int dbdataDbsize(DbdataCursor *pCsr, const char *zSchema){ - DbdataTable *pTab = (DbdataTable*)pCsr->base.pVtab; - char *zSql = 0; - int rc, rc2; - sqlite3_stmt *pStmt = 0; - - zSql = sqlite3_mprintf("PRAGMA %Q.page_count", zSchema); - if( zSql==0 ) return SQLITE_NOMEM; - rc = sqlite3_prepare_v2(pTab->db, zSql, -1, &pStmt, 0); - sqlite3_free(zSql); - if( rc==SQLITE_OK && sqlite3_step(pStmt)==SQLITE_ROW ){ - pCsr->szDb = sqlite3_column_int(pStmt, 0); - } - rc2 = sqlite3_finalize(pStmt); - if( rc==SQLITE_OK ) rc = rc2; - return rc; -} - -/* -** xFilter method for sqlite_dbdata and sqlite_dbptr. -*/ -static int dbdataFilter( - sqlite3_vtab_cursor *pCursor, - int idxNum, const char *idxStr, - int argc, sqlite3_value **argv -){ - DbdataCursor *pCsr = (DbdataCursor*)pCursor; - DbdataTable *pTab = (DbdataTable*)pCursor->pVtab; - int rc = SQLITE_OK; - const char *zSchema = "main"; - - dbdataResetCursor(pCsr); - assert( pCsr->iPgno==1 ); - if( idxNum & 0x01 ){ - zSchema = (const char*)sqlite3_value_text(argv[0]); - } - if( idxNum & 0x02 ){ - pCsr->iPgno = sqlite3_value_int(argv[(idxNum & 0x01)]); - pCsr->bOnePage = 1; - }else{ - pCsr->nPage = dbdataDbsize(pCsr, zSchema); - rc = dbdataDbsize(pCsr, zSchema); - } - - if( rc==SQLITE_OK ){ - if( pTab->pStmt ){ - pCsr->pStmt = pTab->pStmt; - pTab->pStmt = 0; - }else{ - rc = sqlite3_prepare_v2(pTab->db, - "SELECT data FROM sqlite_dbpage(?) WHERE pgno=?", -1, - &pCsr->pStmt, 0 - ); - } - } - if( rc==SQLITE_OK ){ - rc = sqlite3_bind_text(pCsr->pStmt, 1, zSchema, -1, SQLITE_TRANSIENT); - }else{ - pTab->base.zErrMsg = sqlite3_mprintf("%s", sqlite3_errmsg(pTab->db)); - } - if( rc==SQLITE_OK ){ - rc = dbdataNext(pCursor); - } - return rc; -} - -/* -** Return a column for the sqlite_dbdata or sqlite_dbptr table. -*/ -static int dbdataColumn( - sqlite3_vtab_cursor *pCursor, - sqlite3_context *ctx, - int i -){ - DbdataCursor *pCsr = (DbdataCursor*)pCursor; - DbdataTable *pTab = (DbdataTable*)pCursor->pVtab; - if( pTab->bPtr ){ - switch( i ){ - case DBPTR_COLUMN_PGNO: - sqlite3_result_int64(ctx, pCsr->iPgno); - break; - case DBPTR_COLUMN_CHILD: { - int iOff = pCsr->iPgno==1 ? 100 : 0; - if( pCsr->iCell<0 ){ - iOff += 8; - }else{ - iOff += 12 + pCsr->iCell*2; - if( iOff>pCsr->nPage ) return SQLITE_OK; - iOff = get_uint16(&pCsr->aPage[iOff]); - } - if( iOff<=pCsr->nPage ){ - sqlite3_result_int64(ctx, get_uint32(&pCsr->aPage[iOff])); - } - break; - } - } - }else{ - switch( i ){ - case DBDATA_COLUMN_PGNO: - sqlite3_result_int64(ctx, pCsr->iPgno); - break; - case DBDATA_COLUMN_CELL: - sqlite3_result_int(ctx, pCsr->iCell); - break; - case DBDATA_COLUMN_FIELD: - sqlite3_result_int(ctx, pCsr->iField); - break; - case DBDATA_COLUMN_VALUE: { - if( pCsr->iField<0 ){ - sqlite3_result_int64(ctx, pCsr->iIntkey); - }else{ - sqlite3_int64 iType; - dbdataGetVarint(pCsr->pHdrPtr, &iType); - dbdataValue( - ctx, iType, pCsr->pPtr, &pCsr->pRec[pCsr->nRec] - pCsr->pPtr - ); - } - break; - } - } - } - return SQLITE_OK; -} - -/* -** Return the rowid for an sqlite_dbdata or sqlite_dptr table. -*/ -static int dbdataRowid(sqlite3_vtab_cursor *pCursor, sqlite_int64 *pRowid){ - DbdataCursor *pCsr = (DbdataCursor*)pCursor; - *pRowid = pCsr->iRowid; - return SQLITE_OK; -} - - -/* -** Invoke this routine to register the "sqlite_dbdata" virtual table module -*/ -static int sqlite3DbdataRegister(sqlite3 *db){ - static sqlite3_module dbdata_module = { - 0, /* iVersion */ - 0, /* xCreate */ - dbdataConnect, /* xConnect */ - dbdataBestIndex, /* xBestIndex */ - dbdataDisconnect, /* xDisconnect */ - 0, /* xDestroy */ - dbdataOpen, /* xOpen - open a cursor */ - dbdataClose, /* xClose - close a cursor */ - dbdataFilter, /* xFilter - configure scan constraints */ - dbdataNext, /* xNext - advance a cursor */ - dbdataEof, /* xEof - check for end of scan */ - dbdataColumn, /* xColumn - read data */ - dbdataRowid, /* xRowid - read data */ - 0, /* xUpdate */ - 0, /* xBegin */ - 0, /* xSync */ - 0, /* xCommit */ - 0, /* xRollback */ - 0, /* xFindMethod */ - 0, /* xRename */ - 0, /* xSavepoint */ - 0, /* xRelease */ - 0, /* xRollbackTo */ - 0 /* xShadowName */ - }; - - int rc = sqlite3_create_module(db, "sqlite_dbdata", &dbdata_module, 0); - if( rc==SQLITE_OK ){ - rc = sqlite3_create_module(db, "sqlite_dbptr", &dbdata_module, (void*)1); - } - return rc; -} - -#ifdef _WIN32 - -#endif -int sqlite3_dbdata_init( - sqlite3 *db, - char **pzErrMsg, - const sqlite3_api_routines *pApi -){ - SQLITE_EXTENSION_INIT2(pApi); - return sqlite3DbdataRegister(db); -} - -/************************* End ../ext/misc/dbdata.c ********************/ -#endif - -#if defined(SQLITE_ENABLE_SESSION) -/* -** State information for a single open session -*/ -typedef struct OpenSession OpenSession; -struct OpenSession { - char *zName; /* Symbolic name for this session */ - int nFilter; /* Number of xFilter rejection GLOB patterns */ - char **azFilter; /* Array of xFilter rejection GLOB patterns */ - sqlite3_session *p; /* The open session */ -}; -#endif - -typedef struct ExpertInfo ExpertInfo; -struct ExpertInfo { - sqlite3expert *pExpert; - int bVerbose; -}; - -/* A single line in the EQP output */ -typedef struct EQPGraphRow EQPGraphRow; -struct EQPGraphRow { - int iEqpId; /* ID for this row */ - int iParentId; /* ID of the parent row */ - EQPGraphRow *pNext; /* Next row in sequence */ - char zText[1]; /* Text to display for this row */ -}; - -/* All EQP output is collected into an instance of the following */ -typedef struct EQPGraph EQPGraph; -struct EQPGraph { - EQPGraphRow *pRow; /* Linked list of all rows of the EQP output */ - EQPGraphRow *pLast; /* Last element of the pRow list */ - char zPrefix[100]; /* Graph prefix */ -}; - -/* -** State information about the database connection is contained in an -** instance of the following structure. -*/ -typedef struct ShellState ShellState; -struct ShellState { - sqlite3 *db; /* The database */ - u8 autoExplain; /* Automatically turn on .explain mode */ - u8 autoEQP; /* Run EXPLAIN QUERY PLAN prior to seach SQL stmt */ - u8 autoEQPtest; /* autoEQP is in test mode */ - u8 autoEQPtrace; /* autoEQP is in trace mode */ - u8 statsOn; /* True to display memory stats before each finalize */ - u8 scanstatsOn; /* True to display scan stats before each finalize */ - u8 openMode; /* SHELL_OPEN_NORMAL, _APPENDVFS, or _ZIPFILE */ - u8 doXdgOpen; /* Invoke start/open/xdg-open in output_reset() */ - u8 nEqpLevel; /* Depth of the EQP output graph */ - u8 eTraceType; /* SHELL_TRACE_* value for type of trace */ - unsigned mEqpLines; /* Mask of veritical lines in the EQP output graph */ - int outCount; /* Revert to stdout when reaching zero */ - int cnt; /* Number of records displayed so far */ - int lineno; /* Line number of last line read from in */ - int openFlags; /* Additional flags to open. (SQLITE_OPEN_NOFOLLOW) */ - FILE *in; /* Read commands from this stream */ - FILE *out; /* Write results here */ - FILE *traceOut; /* Output for sqlite3_trace() */ - int nErr; /* Number of errors seen */ - int mode; /* An output mode setting */ - int modePrior; /* Saved mode */ - int cMode; /* temporary output mode for the current query */ - int normalMode; /* Output mode before ".explain on" */ - int writableSchema; /* True if PRAGMA writable_schema=ON */ - int showHeader; /* True to show column names in List or Column mode */ - int nCheck; /* Number of ".check" commands run */ - unsigned nProgress; /* Number of progress callbacks encountered */ - unsigned mxProgress; /* Maximum progress callbacks before failing */ - unsigned flgProgress; /* Flags for the progress callback */ - unsigned shellFlgs; /* Various flags */ - unsigned priorShFlgs; /* Saved copy of flags */ - sqlite3_int64 szMax; /* --maxsize argument to .open */ - char *zDestTable; /* Name of destination table when MODE_Insert */ - char *zTempFile; /* Temporary file that might need deleting */ - char zTestcase[30]; /* Name of current test case */ - char colSeparator[20]; /* Column separator character for several modes */ - char rowSeparator[20]; /* Row separator character for MODE_Ascii */ - char colSepPrior[20]; /* Saved column separator */ - char rowSepPrior[20]; /* Saved row separator */ - int *colWidth; /* Requested width of each column in columnar modes */ - int *actualWidth; /* Actual width of each column */ - int nWidth; /* Number of slots in colWidth[] and actualWidth[] */ - char nullValue[20]; /* The text to print when a NULL comes back from - ** the database */ - char outfile[FILENAME_MAX]; /* Filename for *out */ - const char *zDbFilename; /* name of the database file */ - char *zFreeOnClose; /* Filename to free when closing */ - const char *zVfs; /* Name of VFS to use */ - sqlite3_stmt *pStmt; /* Current statement if any. */ - FILE *pLog; /* Write log output here */ - int *aiIndent; /* Array of indents used in MODE_Explain */ - int nIndent; /* Size of array aiIndent[] */ - int iIndent; /* Index of current op in aiIndent[] */ - EQPGraph sGraph; /* Information for the graphical EXPLAIN QUERY PLAN */ -#if defined(SQLITE_ENABLE_SESSION) - int nSession; /* Number of active sessions */ - OpenSession aSession[4]; /* Array of sessions. [0] is in focus. */ -#endif - ExpertInfo expert; /* Valid if previous command was ".expert OPT..." */ -}; - - -/* Allowed values for ShellState.autoEQP -*/ -#define AUTOEQP_off 0 /* Automatic EXPLAIN QUERY PLAN is off */ -#define AUTOEQP_on 1 /* Automatic EQP is on */ -#define AUTOEQP_trigger 2 /* On and also show plans for triggers */ -#define AUTOEQP_full 3 /* Show full EXPLAIN */ - -/* Allowed values for ShellState.openMode -*/ -#define SHELL_OPEN_UNSPEC 0 /* No open-mode specified */ -#define SHELL_OPEN_NORMAL 1 /* Normal database file */ -#define SHELL_OPEN_APPENDVFS 2 /* Use appendvfs */ -#define SHELL_OPEN_ZIPFILE 3 /* Use the zipfile virtual table */ -#define SHELL_OPEN_READONLY 4 /* Open a normal database read-only */ -#define SHELL_OPEN_DESERIALIZE 5 /* Open using sqlite3_deserialize() */ -#define SHELL_OPEN_HEXDB 6 /* Use "dbtotxt" output as data source */ - -/* Allowed values for ShellState.eTraceType -*/ -#define SHELL_TRACE_PLAIN 0 /* Show input SQL text */ -#define SHELL_TRACE_EXPANDED 1 /* Show expanded SQL text */ -#define SHELL_TRACE_NORMALIZED 2 /* Show normalized SQL text */ - -/* Bits in the ShellState.flgProgress variable */ -#define SHELL_PROGRESS_QUIET 0x01 /* Omit announcing every progress callback */ -#define SHELL_PROGRESS_RESET 0x02 /* Reset the count when the progres - ** callback limit is reached, and for each - ** top-level SQL statement */ -#define SHELL_PROGRESS_ONCE 0x04 /* Cancel the --limit after firing once */ - -/* -** These are the allowed shellFlgs values -*/ -#define SHFLG_Pagecache 0x00000001 /* The --pagecache option is used */ -#define SHFLG_Lookaside 0x00000002 /* Lookaside memory is used */ -#define SHFLG_Backslash 0x00000004 /* The --backslash option is used */ -#define SHFLG_PreserveRowid 0x00000008 /* .dump preserves rowid values */ -#define SHFLG_Newlines 0x00000010 /* .dump --newline flag */ -#define SHFLG_CountChanges 0x00000020 /* .changes setting */ -#define SHFLG_Echo 0x00000040 /* .echo or --echo setting */ -#define SHFLG_HeaderSet 0x00000080 /* .header has been used */ - -/* -** Macros for testing and setting shellFlgs -*/ -#define ShellHasFlag(P,X) (((P)->shellFlgs & (X))!=0) -#define ShellSetFlag(P,X) ((P)->shellFlgs|=(X)) -#define ShellClearFlag(P,X) ((P)->shellFlgs&=(~(X))) - -/* -** These are the allowed modes. -*/ -#define MODE_Line 0 /* One column per line. Blank line between records */ -#define MODE_Column 1 /* One record per line in neat columns */ -#define MODE_List 2 /* One record per line with a separator */ -#define MODE_Semi 3 /* Same as MODE_List but append ";" to each line */ -#define MODE_Html 4 /* Generate an XHTML table */ -#define MODE_Insert 5 /* Generate SQL "insert" statements */ -#define MODE_Quote 6 /* Quote values as for SQL */ -#define MODE_Tcl 7 /* Generate ANSI-C or TCL quoted elements */ -#define MODE_Csv 8 /* Quote strings, numbers are plain */ -#define MODE_Explain 9 /* Like MODE_Column, but do not truncate data */ -#define MODE_Ascii 10 /* Use ASCII unit and record separators (0x1F/0x1E) */ -#define MODE_Pretty 11 /* Pretty-print schemas */ -#define MODE_EQP 12 /* Converts EXPLAIN QUERY PLAN output into a graph */ -#define MODE_Json 13 /* Output JSON */ -#define MODE_Markdown 14 /* Markdown formatting */ -#define MODE_Table 15 /* MySQL-style table formatting */ -#define MODE_Box 16 /* Unicode box-drawing characters */ - -static const char *modeDescr[] = { - "line", - "column", - "list", - "semi", - "html", - "insert", - "quote", - "tcl", - "csv", - "explain", - "ascii", - "prettyprint", - "eqp", - "json", - "markdown", - "table", - "box" -}; - -/* -** These are the column/row/line separators used by the various -** import/export modes. -*/ -#define SEP_Column "|" -#define SEP_Row "\n" -#define SEP_Tab "\t" -#define SEP_Space " " -#define SEP_Comma "," -#define SEP_CrLf "\r\n" -#define SEP_Unit "\x1F" -#define SEP_Record "\x1E" - -/* -** A callback for the sqlite3_log() interface. -*/ -static void shellLog(void *pArg, int iErrCode, const char *zMsg){ - ShellState *p = (ShellState*)pArg; - if( p->pLog==0 ) return; - utf8_printf(p->pLog, "(%d) %s\n", iErrCode, zMsg); - fflush(p->pLog); -} - -/* -** SQL function: shell_putsnl(X) -** -** Write the text X to the screen (or whatever output is being directed) -** adding a newline at the end, and then return X. -*/ -static void shellPutsFunc( - sqlite3_context *pCtx, - int nVal, - sqlite3_value **apVal -){ - ShellState *p = (ShellState*)sqlite3_user_data(pCtx); - (void)nVal; - utf8_printf(p->out, "%s\n", sqlite3_value_text(apVal[0])); - sqlite3_result_value(pCtx, apVal[0]); -} - -/* -** SQL function: edit(VALUE) -** edit(VALUE,EDITOR) -** -** These steps: -** -** (1) Write VALUE into a temporary file. -** (2) Run program EDITOR on that temporary file. -** (3) Read the temporary file back and return its content as the result. -** (4) Delete the temporary file -** -** If the EDITOR argument is omitted, use the value in the VISUAL -** environment variable. If still there is no EDITOR, through an error. -** -** Also throw an error if the EDITOR program returns a non-zero exit code. -*/ -#ifndef SQLITE_NOHAVE_SYSTEM -static void editFunc( - sqlite3_context *context, - int argc, - sqlite3_value **argv -){ - const char *zEditor; - char *zTempFile = 0; - sqlite3 *db; - char *zCmd = 0; - int bBin; - int rc; - int hasCRNL = 0; - FILE *f = 0; - sqlite3_int64 sz; - sqlite3_int64 x; - unsigned char *p = 0; - - if( argc==2 ){ - zEditor = (const char*)sqlite3_value_text(argv[1]); - }else{ - zEditor = getenv("VISUAL"); - } - if( zEditor==0 ){ - sqlite3_result_error(context, "no editor for edit()", -1); - return; - } - if( sqlite3_value_type(argv[0])==SQLITE_NULL ){ - sqlite3_result_error(context, "NULL input to edit()", -1); - return; - } - db = sqlite3_context_db_handle(context); - zTempFile = 0; - sqlite3_file_control(db, 0, SQLITE_FCNTL_TEMPFILENAME, &zTempFile); - if( zTempFile==0 ){ - sqlite3_uint64 r = 0; - sqlite3_randomness(sizeof(r), &r); - zTempFile = sqlite3_mprintf("temp%llx", r); - if( zTempFile==0 ){ - sqlite3_result_error_nomem(context); - return; - } - } - bBin = sqlite3_value_type(argv[0])==SQLITE_BLOB; - /* When writing the file to be edited, do \n to \r\n conversions on systems - ** that want \r\n line endings */ - f = fopen(zTempFile, bBin ? "wb" : "w"); - if( f==0 ){ - sqlite3_result_error(context, "edit() cannot open temp file", -1); - goto edit_func_end; - } - sz = sqlite3_value_bytes(argv[0]); - if( bBin ){ - x = fwrite(sqlite3_value_blob(argv[0]), 1, (size_t)sz, f); - }else{ - const char *z = (const char*)sqlite3_value_text(argv[0]); - /* Remember whether or not the value originally contained \r\n */ - if( z && strstr(z,"\r\n")!=0 ) hasCRNL = 1; - x = fwrite(sqlite3_value_text(argv[0]), 1, (size_t)sz, f); - } - fclose(f); - f = 0; - if( x!=sz ){ - sqlite3_result_error(context, "edit() could not write the whole file", -1); - goto edit_func_end; - } - zCmd = sqlite3_mprintf("%s \"%s\"", zEditor, zTempFile); - if( zCmd==0 ){ - sqlite3_result_error_nomem(context); - goto edit_func_end; - } - rc = system(zCmd); - sqlite3_free(zCmd); - if( rc ){ - sqlite3_result_error(context, "EDITOR returned non-zero", -1); - goto edit_func_end; - } - f = fopen(zTempFile, "rb"); - if( f==0 ){ - sqlite3_result_error(context, - "edit() cannot reopen temp file after edit", -1); - goto edit_func_end; - } - fseek(f, 0, SEEK_END); - sz = ftell(f); - rewind(f); - p = sqlite3_malloc64( sz+1 ); - if( p==0 ){ - sqlite3_result_error_nomem(context); - goto edit_func_end; - } - x = fread(p, 1, (size_t)sz, f); - fclose(f); - f = 0; - if( x!=sz ){ - sqlite3_result_error(context, "could not read back the whole file", -1); - goto edit_func_end; - } - if( bBin ){ - sqlite3_result_blob64(context, p, sz, sqlite3_free); - }else{ - sqlite3_int64 i, j; - if( hasCRNL ){ - /* If the original contains \r\n then do no conversions back to \n */ - j = sz; - }else{ - /* If the file did not originally contain \r\n then convert any new - ** \r\n back into \n */ - for(i=j=0; imodePrior = p->mode; - p->priorShFlgs = p->shellFlgs; - memcpy(p->colSepPrior, p->colSeparator, sizeof(p->colSeparator)); - memcpy(p->rowSepPrior, p->rowSeparator, sizeof(p->rowSeparator)); -} -static void outputModePop(ShellState *p){ - p->mode = p->modePrior; - p->shellFlgs = p->priorShFlgs; - memcpy(p->colSeparator, p->colSepPrior, sizeof(p->colSeparator)); - memcpy(p->rowSeparator, p->rowSepPrior, sizeof(p->rowSeparator)); -} - -/* -** Output the given string as a hex-encoded blob (eg. X'1234' ) -*/ -static void output_hex_blob(FILE *out, const void *pBlob, int nBlob){ - int i; - char *zBlob = (char *)pBlob; - raw_printf(out,"X'"); - for(i=0; i0 ){ - utf8_printf(out,"%.*s",i,z); - } - if( z[i]=='<' ){ - raw_printf(out,"<"); - }else if( z[i]=='&' ){ - raw_printf(out,"&"); - }else if( z[i]=='>' ){ - raw_printf(out,">"); - }else if( z[i]=='\"' ){ - raw_printf(out,"""); - }else if( z[i]=='\'' ){ - raw_printf(out,"'"); - }else{ - break; - } - z += i + 1; - } -} - -/* -** If a field contains any character identified by a 1 in the following -** array, then the string must be quoted for CSV. -*/ -static const char needCsvQuote[] = { - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -}; - -/* -** Output a single term of CSV. Actually, p->colSeparator is used for -** the separator, which may or may not be a comma. p->nullValue is -** the null value. Strings are quoted if necessary. The separator -** is only issued if bSep is true. -*/ -static void output_csv(ShellState *p, const char *z, int bSep){ - FILE *out = p->out; - if( z==0 ){ - utf8_printf(out,"%s",p->nullValue); - }else{ - int i; - int nSep = strlen30(p->colSeparator); - for(i=0; z[i]; i++){ - if( needCsvQuote[((unsigned char*)z)[i]] - || (z[i]==p->colSeparator[0] && - (nSep==1 || memcmp(z, p->colSeparator, nSep)==0)) ){ - i = 0; - break; - } - } - if( i==0 ){ - char *zQuoted = sqlite3_mprintf("\"%w\"", z); - utf8_printf(out, "%s", zQuoted); - sqlite3_free(zQuoted); - }else{ - utf8_printf(out, "%s", z); - } - } - if( bSep ){ - utf8_printf(p->out, "%s", p->colSeparator); - } -} - -/* -** This routine runs when the user presses Ctrl-C -*/ -static void interrupt_handler(int NotUsed){ - UNUSED_PARAMETER(NotUsed); - seenInterrupt++; - if( seenInterrupt>2 ) exit(1); - if( globalDb ) sqlite3_interrupt(globalDb); -} - -#if (defined(_WIN32) || defined(WIN32)) && !defined(_WIN32_WCE) -/* -** This routine runs for console events (e.g. Ctrl-C) on Win32 -*/ -static BOOL WINAPI ConsoleCtrlHandler( - DWORD dwCtrlType /* One of the CTRL_*_EVENT constants */ -){ - if( dwCtrlType==CTRL_C_EVENT ){ - interrupt_handler(0); - return TRUE; - } - return FALSE; -} -#endif - -#ifndef SQLITE_OMIT_AUTHORIZATION -/* -** When the ".auth ON" is set, the following authorizer callback is -** invoked. It always returns SQLITE_OK. -*/ -static int shellAuth( - void *pClientData, - int op, - const char *zA1, - const char *zA2, - const char *zA3, - const char *zA4 -){ - ShellState *p = (ShellState*)pClientData; - static const char *azAction[] = { 0, - "CREATE_INDEX", "CREATE_TABLE", "CREATE_TEMP_INDEX", - "CREATE_TEMP_TABLE", "CREATE_TEMP_TRIGGER", "CREATE_TEMP_VIEW", - "CREATE_TRIGGER", "CREATE_VIEW", "DELETE", - "DROP_INDEX", "DROP_TABLE", "DROP_TEMP_INDEX", - "DROP_TEMP_TABLE", "DROP_TEMP_TRIGGER", "DROP_TEMP_VIEW", - "DROP_TRIGGER", "DROP_VIEW", "INSERT", - "PRAGMA", "READ", "SELECT", - "TRANSACTION", "UPDATE", "ATTACH", - "DETACH", "ALTER_TABLE", "REINDEX", - "ANALYZE", "CREATE_VTABLE", "DROP_VTABLE", - "FUNCTION", "SAVEPOINT", "RECURSIVE" - }; - int i; - const char *az[4]; - az[0] = zA1; - az[1] = zA2; - az[2] = zA3; - az[3] = zA4; - utf8_printf(p->out, "authorizer: %s", azAction[op]); - for(i=0; i<4; i++){ - raw_printf(p->out, " "); - if( az[i] ){ - output_c_string(p->out, az[i]); - }else{ - raw_printf(p->out, "NULL"); - } - } - raw_printf(p->out, "\n"); - return SQLITE_OK; -} -#endif - -/* -** Print a schema statement. Part of MODE_Semi and MODE_Pretty output. -** -** This routine converts some CREATE TABLE statements for shadow tables -** in FTS3/4/5 into CREATE TABLE IF NOT EXISTS statements. -*/ -static void printSchemaLine(FILE *out, const char *z, const char *zTail){ - if( z==0 ) return; - if( zTail==0 ) return; - if( sqlite3_strglob("CREATE TABLE ['\"]*", z)==0 ){ - utf8_printf(out, "CREATE TABLE IF NOT EXISTS %s%s", z+13, zTail); - }else{ - utf8_printf(out, "%s%s", z, zTail); - } -} -static void printSchemaLineN(FILE *out, char *z, int n, const char *zTail){ - char c = z[n]; - z[n] = 0; - printSchemaLine(out, z, zTail); - z[n] = c; -} - -/* -** Return true if string z[] has nothing but whitespace and comments to the -** end of the first line. -*/ -static int wsToEol(const char *z){ - int i; - for(i=0; z[i]; i++){ - if( z[i]=='\n' ) return 1; - if( IsSpace(z[i]) ) continue; - if( z[i]=='-' && z[i+1]=='-' ) return 1; - return 0; - } - return 1; -} - -/* -** Add a new entry to the EXPLAIN QUERY PLAN data -*/ -static void eqp_append(ShellState *p, int iEqpId, int p2, const char *zText){ - EQPGraphRow *pNew; - int nText = strlen30(zText); - if( p->autoEQPtest ){ - utf8_printf(p->out, "%d,%d,%s\n", iEqpId, p2, zText); - } - pNew = sqlite3_malloc64( sizeof(*pNew) + nText ); - if( pNew==0 ) shell_out_of_memory(); - pNew->iEqpId = iEqpId; - pNew->iParentId = p2; - memcpy(pNew->zText, zText, nText+1); - pNew->pNext = 0; - if( p->sGraph.pLast ){ - p->sGraph.pLast->pNext = pNew; - }else{ - p->sGraph.pRow = pNew; - } - p->sGraph.pLast = pNew; -} - -/* -** Free and reset the EXPLAIN QUERY PLAN data that has been collected -** in p->sGraph. -*/ -static void eqp_reset(ShellState *p){ - EQPGraphRow *pRow, *pNext; - for(pRow = p->sGraph.pRow; pRow; pRow = pNext){ - pNext = pRow->pNext; - sqlite3_free(pRow); - } - memset(&p->sGraph, 0, sizeof(p->sGraph)); -} - -/* Return the next EXPLAIN QUERY PLAN line with iEqpId that occurs after -** pOld, or return the first such line if pOld is NULL -*/ -static EQPGraphRow *eqp_next_row(ShellState *p, int iEqpId, EQPGraphRow *pOld){ - EQPGraphRow *pRow = pOld ? pOld->pNext : p->sGraph.pRow; - while( pRow && pRow->iParentId!=iEqpId ) pRow = pRow->pNext; - return pRow; -} - -/* Render a single level of the graph that has iEqpId as its parent. Called -** recursively to render sublevels. -*/ -static void eqp_render_level(ShellState *p, int iEqpId){ - EQPGraphRow *pRow, *pNext; - int n = strlen30(p->sGraph.zPrefix); - char *z; - for(pRow = eqp_next_row(p, iEqpId, 0); pRow; pRow = pNext){ - pNext = eqp_next_row(p, iEqpId, pRow); - z = pRow->zText; - utf8_printf(p->out, "%s%s%s\n", p->sGraph.zPrefix, - pNext ? "|--" : "`--", z); - if( n<(int)sizeof(p->sGraph.zPrefix)-7 ){ - memcpy(&p->sGraph.zPrefix[n], pNext ? "| " : " ", 4); - eqp_render_level(p, pRow->iEqpId); - p->sGraph.zPrefix[n] = 0; - } - } -} - -/* -** Display and reset the EXPLAIN QUERY PLAN data -*/ -static void eqp_render(ShellState *p){ - EQPGraphRow *pRow = p->sGraph.pRow; - if( pRow ){ - if( pRow->zText[0]=='-' ){ - if( pRow->pNext==0 ){ - eqp_reset(p); - return; - } - utf8_printf(p->out, "%s\n", pRow->zText+3); - p->sGraph.pRow = pRow->pNext; - sqlite3_free(pRow); - }else{ - utf8_printf(p->out, "QUERY PLAN\n"); - } - p->sGraph.zPrefix[0] = 0; - eqp_render_level(p, 0); - eqp_reset(p); - } -} - -#ifndef SQLITE_OMIT_PROGRESS_CALLBACK -/* -** Progress handler callback. -*/ -static int progress_handler(void *pClientData) { - ShellState *p = (ShellState*)pClientData; - p->nProgress++; - if( p->nProgress>=p->mxProgress && p->mxProgress>0 ){ - raw_printf(p->out, "Progress limit reached (%u)\n", p->nProgress); - if( p->flgProgress & SHELL_PROGRESS_RESET ) p->nProgress = 0; - if( p->flgProgress & SHELL_PROGRESS_ONCE ) p->mxProgress = 0; - return 1; - } - if( (p->flgProgress & SHELL_PROGRESS_QUIET)==0 ){ - raw_printf(p->out, "Progress %u\n", p->nProgress); - } - return 0; -} -#endif /* SQLITE_OMIT_PROGRESS_CALLBACK */ - -/* -** Print N dashes -*/ -static void print_dashes(FILE *out, int N){ - const char zDash[] = "--------------------------------------------------"; - const int nDash = sizeof(zDash) - 1; - while( N>nDash ){ - fputs(zDash, out); - N -= nDash; - } - raw_printf(out, "%.*s", N, zDash); -} - -/* -** Print a markdown or table-style row separator using ascii-art -*/ -static void print_row_separator( - ShellState *p, - int nArg, - const char *zSep -){ - int i; - if( nArg>0 ){ - fputs(zSep, p->out); - print_dashes(p->out, p->actualWidth[0]+2); - for(i=1; iout); - print_dashes(p->out, p->actualWidth[i]+2); - } - fputs(zSep, p->out); - } - fputs("\n", p->out); -} - -/* -** This is the callback routine that the shell -** invokes for each row of a query result. -*/ -static int shell_callback( - void *pArg, - int nArg, /* Number of result columns */ - char **azArg, /* Text of each result column */ - char **azCol, /* Column names */ - int *aiType /* Column types. Might be NULL */ -){ - int i; - ShellState *p = (ShellState*)pArg; - - if( azArg==0 ) return 0; - switch( p->cMode ){ - case MODE_Line: { - int w = 5; - if( azArg==0 ) break; - for(i=0; iw ) w = len; - } - if( p->cnt++>0 ) utf8_printf(p->out, "%s", p->rowSeparator); - for(i=0; iout,"%*s = %s%s", w, azCol[i], - azArg[i] ? azArg[i] : p->nullValue, p->rowSeparator); - } - break; - } - case MODE_Explain: { - static const int aExplainWidth[] = {4, 13, 4, 4, 4, 13, 2, 13}; - if( nArg>ArraySize(aExplainWidth) ){ - nArg = ArraySize(aExplainWidth); - } - if( p->cnt++==0 ){ - for(i=0; iout, w, azCol[i]); - fputs(i==nArg-1 ? "\n" : " ", p->out); - } - for(i=0; iout, w); - fputs(i==nArg-1 ? "\n" : " ", p->out); - } - } - if( azArg==0 ) break; - for(i=0; iw ){ - w = strlenChar(azArg[i]); - } - if( i==1 && p->aiIndent && p->pStmt ){ - if( p->iIndentnIndent ){ - utf8_printf(p->out, "%*.s", p->aiIndent[p->iIndent], ""); - } - p->iIndent++; - } - utf8_width_print(p->out, w, azArg[i] ? azArg[i] : p->nullValue); - fputs(i==nArg-1 ? "\n" : " ", p->out); - } - break; - } - case MODE_Semi: { /* .schema and .fullschema output */ - printSchemaLine(p->out, azArg[0], ";\n"); - break; - } - case MODE_Pretty: { /* .schema and .fullschema with --indent */ - char *z; - int j; - int nParen = 0; - char cEnd = 0; - char c; - int nLine = 0; - assert( nArg==1 ); - if( azArg[0]==0 ) break; - if( sqlite3_strlike("CREATE VIEW%", azArg[0], 0)==0 - || sqlite3_strlike("CREATE TRIG%", azArg[0], 0)==0 - ){ - utf8_printf(p->out, "%s;\n", azArg[0]); - break; - } - z = sqlite3_mprintf("%s", azArg[0]); - j = 0; - for(i=0; IsSpace(z[i]); i++){} - for(; (c = z[i])!=0; i++){ - if( IsSpace(c) ){ - if( z[j-1]=='\r' ) z[j-1] = '\n'; - if( IsSpace(z[j-1]) || z[j-1]=='(' ) continue; - }else if( (c=='(' || c==')') && j>0 && IsSpace(z[j-1]) ){ - j--; - } - z[j++] = c; - } - while( j>0 && IsSpace(z[j-1]) ){ j--; } - z[j] = 0; - if( strlen30(z)>=79 ){ - for(i=j=0; (c = z[i])!=0; i++){ /* Copy from z[i] back to z[j] */ - if( c==cEnd ){ - cEnd = 0; - }else if( c=='"' || c=='\'' || c=='`' ){ - cEnd = c; - }else if( c=='[' ){ - cEnd = ']'; - }else if( c=='-' && z[i+1]=='-' ){ - cEnd = '\n'; - }else if( c=='(' ){ - nParen++; - }else if( c==')' ){ - nParen--; - if( nLine>0 && nParen==0 && j>0 ){ - printSchemaLineN(p->out, z, j, "\n"); - j = 0; - } - } - z[j++] = c; - if( nParen==1 && cEnd==0 - && (c=='(' || c=='\n' || (c==',' && !wsToEol(z+i+1))) - ){ - if( c=='\n' ) j--; - printSchemaLineN(p->out, z, j, "\n "); - j = 0; - nLine++; - while( IsSpace(z[i+1]) ){ i++; } - } - } - z[j] = 0; - } - printSchemaLine(p->out, z, ";\n"); - sqlite3_free(z); - break; - } - case MODE_List: { - if( p->cnt++==0 && p->showHeader ){ - for(i=0; iout,"%s%s",azCol[i], - i==nArg-1 ? p->rowSeparator : p->colSeparator); - } - } - if( azArg==0 ) break; - for(i=0; inullValue; - utf8_printf(p->out, "%s", z); - if( iout, "%s", p->colSeparator); - }else{ - utf8_printf(p->out, "%s", p->rowSeparator); - } - } - break; - } - case MODE_Html: { - if( p->cnt++==0 && p->showHeader ){ - raw_printf(p->out,""); - for(i=0; iout,""); - output_html_string(p->out, azCol[i]); - raw_printf(p->out,"\n"); - } - raw_printf(p->out,"\n"); - } - if( azArg==0 ) break; - raw_printf(p->out,""); - for(i=0; iout,""); - output_html_string(p->out, azArg[i] ? azArg[i] : p->nullValue); - raw_printf(p->out,"\n"); - } - raw_printf(p->out,"\n"); - break; - } - case MODE_Tcl: { - if( p->cnt++==0 && p->showHeader ){ - for(i=0; iout,azCol[i] ? azCol[i] : ""); - if(iout, "%s", p->colSeparator); - } - utf8_printf(p->out, "%s", p->rowSeparator); - } - if( azArg==0 ) break; - for(i=0; iout, azArg[i] ? azArg[i] : p->nullValue); - if(iout, "%s", p->colSeparator); - } - utf8_printf(p->out, "%s", p->rowSeparator); - break; - } - case MODE_Csv: { - setBinaryMode(p->out, 1); - if( p->cnt++==0 && p->showHeader ){ - for(i=0; iout, "%s", p->rowSeparator); - } - if( nArg>0 ){ - for(i=0; iout, "%s", p->rowSeparator); - } - setTextMode(p->out, 1); - break; - } - case MODE_Insert: { - if( azArg==0 ) break; - utf8_printf(p->out,"INSERT INTO %s",p->zDestTable); - if( p->showHeader ){ - raw_printf(p->out,"("); - for(i=0; i0 ) raw_printf(p->out, ","); - if( quoteChar(azCol[i]) ){ - char *z = sqlite3_mprintf("\"%w\"", azCol[i]); - utf8_printf(p->out, "%s", z); - sqlite3_free(z); - }else{ - raw_printf(p->out, "%s", azCol[i]); - } - } - raw_printf(p->out,")"); - } - p->cnt++; - for(i=0; iout, i>0 ? "," : " VALUES("); - if( (azArg[i]==0) || (aiType && aiType[i]==SQLITE_NULL) ){ - utf8_printf(p->out,"NULL"); - }else if( aiType && aiType[i]==SQLITE_TEXT ){ - if( ShellHasFlag(p, SHFLG_Newlines) ){ - output_quoted_string(p->out, azArg[i]); - }else{ - output_quoted_escaped_string(p->out, azArg[i]); - } - }else if( aiType && aiType[i]==SQLITE_INTEGER ){ - utf8_printf(p->out,"%s", azArg[i]); - }else if( aiType && aiType[i]==SQLITE_FLOAT ){ - char z[50]; - double r = sqlite3_column_double(p->pStmt, i); - sqlite3_uint64 ur; - memcpy(&ur,&r,sizeof(r)); - if( ur==0x7ff0000000000000LL ){ - raw_printf(p->out, "1e999"); - }else if( ur==0xfff0000000000000LL ){ - raw_printf(p->out, "-1e999"); - }else{ - sqlite3_snprintf(50,z,"%!.20g", r); - raw_printf(p->out, "%s", z); - } - }else if( aiType && aiType[i]==SQLITE_BLOB && p->pStmt ){ - const void *pBlob = sqlite3_column_blob(p->pStmt, i); - int nBlob = sqlite3_column_bytes(p->pStmt, i); - output_hex_blob(p->out, pBlob, nBlob); - }else if( isNumber(azArg[i], 0) ){ - utf8_printf(p->out,"%s", azArg[i]); - }else if( ShellHasFlag(p, SHFLG_Newlines) ){ - output_quoted_string(p->out, azArg[i]); - }else{ - output_quoted_escaped_string(p->out, azArg[i]); - } - } - raw_printf(p->out,");\n"); - break; - } - case MODE_Json: { - if( azArg==0 ) break; - if( p->cnt==0 ){ - fputs("[{", p->out); - }else{ - fputs(",\n{", p->out); - } - p->cnt++; - for(i=0; iout, azCol[i], -1); - putc(':', p->out); - if( (azArg[i]==0) || (aiType && aiType[i]==SQLITE_NULL) ){ - fputs("null",p->out); - }else if( aiType && aiType[i]==SQLITE_FLOAT ){ - char z[50]; - double r = sqlite3_column_double(p->pStmt, i); - sqlite3_uint64 ur; - memcpy(&ur,&r,sizeof(r)); - if( ur==0x7ff0000000000000LL ){ - raw_printf(p->out, "1e999"); - }else if( ur==0xfff0000000000000LL ){ - raw_printf(p->out, "-1e999"); - }else{ - sqlite3_snprintf(50,z,"%!.20g", r); - raw_printf(p->out, "%s", z); - } - }else if( aiType && aiType[i]==SQLITE_BLOB && p->pStmt ){ - const void *pBlob = sqlite3_column_blob(p->pStmt, i); - int nBlob = sqlite3_column_bytes(p->pStmt, i); - output_json_string(p->out, pBlob, nBlob); - }else if( aiType && aiType[i]==SQLITE_TEXT ){ - output_json_string(p->out, azArg[i], -1); - }else{ - utf8_printf(p->out,"%s", azArg[i]); - } - if( iout); - } - } - putc('}', p->out); - break; - } - case MODE_Quote: { - if( azArg==0 ) break; - if( p->cnt==0 && p->showHeader ){ - for(i=0; i0 ) fputs(p->colSeparator, p->out); - output_quoted_string(p->out, azCol[i]); - } - fputs(p->rowSeparator, p->out); - } - p->cnt++; - for(i=0; i0 ) fputs(p->colSeparator, p->out); - if( (azArg[i]==0) || (aiType && aiType[i]==SQLITE_NULL) ){ - utf8_printf(p->out,"NULL"); - }else if( aiType && aiType[i]==SQLITE_TEXT ){ - output_quoted_string(p->out, azArg[i]); - }else if( aiType && aiType[i]==SQLITE_INTEGER ){ - utf8_printf(p->out,"%s", azArg[i]); - }else if( aiType && aiType[i]==SQLITE_FLOAT ){ - char z[50]; - double r = sqlite3_column_double(p->pStmt, i); - sqlite3_snprintf(50,z,"%!.20g", r); - raw_printf(p->out, "%s", z); - }else if( aiType && aiType[i]==SQLITE_BLOB && p->pStmt ){ - const void *pBlob = sqlite3_column_blob(p->pStmt, i); - int nBlob = sqlite3_column_bytes(p->pStmt, i); - output_hex_blob(p->out, pBlob, nBlob); - }else if( isNumber(azArg[i], 0) ){ - utf8_printf(p->out,"%s", azArg[i]); - }else{ - output_quoted_string(p->out, azArg[i]); - } - } - fputs(p->rowSeparator, p->out); - break; - } - case MODE_Ascii: { - if( p->cnt++==0 && p->showHeader ){ - for(i=0; i0 ) utf8_printf(p->out, "%s", p->colSeparator); - utf8_printf(p->out,"%s",azCol[i] ? azCol[i] : ""); - } - utf8_printf(p->out, "%s", p->rowSeparator); - } - if( azArg==0 ) break; - for(i=0; i0 ) utf8_printf(p->out, "%s", p->colSeparator); - utf8_printf(p->out,"%s",azArg[i] ? azArg[i] : p->nullValue); - } - utf8_printf(p->out, "%s", p->rowSeparator); - break; - } - case MODE_EQP: { - eqp_append(p, atoi(azArg[0]), atoi(azArg[1]), azArg[3]); - break; - } - } - return 0; -} - -/* -** This is the callback routine that the SQLite library -** invokes for each row of a query result. -*/ -static int callback(void *pArg, int nArg, char **azArg, char **azCol){ - /* since we don't have type info, call the shell_callback with a NULL value */ - return shell_callback(pArg, nArg, azArg, azCol, NULL); -} - -/* -** This is the callback routine from sqlite3_exec() that appends all -** output onto the end of a ShellText object. -*/ -static int captureOutputCallback(void *pArg, int nArg, char **azArg, char **az){ - ShellText *p = (ShellText*)pArg; - int i; - UNUSED_PARAMETER(az); - if( azArg==0 ) return 0; - if( p->n ) appendText(p, "|", 0); - for(i=0; idb, - "SAVEPOINT selftest_init;\n" - "CREATE TABLE IF NOT EXISTS selftest(\n" - " tno INTEGER PRIMARY KEY,\n" /* Test number */ - " op TEXT,\n" /* Operator: memo run */ - " cmd TEXT,\n" /* Command text */ - " ans TEXT\n" /* Desired answer */ - ");" - "CREATE TEMP TABLE [_shell$self](op,cmd,ans);\n" - "INSERT INTO [_shell$self](rowid,op,cmd)\n" - " VALUES(coalesce((SELECT (max(tno)+100)/10 FROM selftest),10),\n" - " 'memo','Tests generated by --init');\n" - "INSERT INTO [_shell$self]\n" - " SELECT 'run',\n" - " 'SELECT hex(sha3_query(''SELECT type,name,tbl_name,sql " - "FROM sqlite_schema ORDER BY 2'',224))',\n" - " hex(sha3_query('SELECT type,name,tbl_name,sql " - "FROM sqlite_schema ORDER BY 2',224));\n" - "INSERT INTO [_shell$self]\n" - " SELECT 'run'," - " 'SELECT hex(sha3_query(''SELECT * FROM \"' ||" - " printf('%w',name) || '\" NOT INDEXED'',224))',\n" - " hex(sha3_query(printf('SELECT * FROM \"%w\" NOT INDEXED',name),224))\n" - " FROM (\n" - " SELECT name FROM sqlite_schema\n" - " WHERE type='table'\n" - " AND name<>'selftest'\n" - " AND coalesce(rootpage,0)>0\n" - " )\n" - " ORDER BY name;\n" - "INSERT INTO [_shell$self]\n" - " VALUES('run','PRAGMA integrity_check','ok');\n" - "INSERT INTO selftest(tno,op,cmd,ans)" - " SELECT rowid*10,op,cmd,ans FROM [_shell$self];\n" - "DROP TABLE [_shell$self];" - ,0,0,&zErrMsg); - if( zErrMsg ){ - utf8_printf(stderr, "SELFTEST initialization failure: %s\n", zErrMsg); - sqlite3_free(zErrMsg); - } - sqlite3_exec(p->db, "RELEASE selftest_init",0,0,0); -} - - -/* -** Set the destination table field of the ShellState structure to -** the name of the table given. Escape any quote characters in the -** table name. -*/ -static void set_table_name(ShellState *p, const char *zName){ - int i, n; - char cQuote; - char *z; - - if( p->zDestTable ){ - free(p->zDestTable); - p->zDestTable = 0; - } - if( zName==0 ) return; - cQuote = quoteChar(zName); - n = strlen30(zName); - if( cQuote ) n += n+2; - z = p->zDestTable = malloc( n+1 ); - if( z==0 ) shell_out_of_memory(); - n = 0; - if( cQuote ) z[n++] = cQuote; - for(i=0; zName[i]; i++){ - z[n++] = zName[i]; - if( zName[i]==cQuote ) z[n++] = cQuote; - } - if( cQuote ) z[n++] = cQuote; - z[n] = 0; -} - - -/* -** Execute a query statement that will generate SQL output. Print -** the result columns, comma-separated, on a line and then add a -** semicolon terminator to the end of that line. -** -** If the number of columns is 1 and that column contains text "--" -** then write the semicolon on a separate line. That way, if a -** "--" comment occurs at the end of the statement, the comment -** won't consume the semicolon terminator. -*/ -static int run_table_dump_query( - ShellState *p, /* Query context */ - const char *zSelect /* SELECT statement to extract content */ -){ - sqlite3_stmt *pSelect; - int rc; - int nResult; - int i; - const char *z; - rc = sqlite3_prepare_v2(p->db, zSelect, -1, &pSelect, 0); - if( rc!=SQLITE_OK || !pSelect ){ - utf8_printf(p->out, "/**** ERROR: (%d) %s *****/\n", rc, - sqlite3_errmsg(p->db)); - if( (rc&0xff)!=SQLITE_CORRUPT ) p->nErr++; - return rc; - } - rc = sqlite3_step(pSelect); - nResult = sqlite3_column_count(pSelect); - while( rc==SQLITE_ROW ){ - z = (const char*)sqlite3_column_text(pSelect, 0); - utf8_printf(p->out, "%s", z); - for(i=1; iout, ",%s", sqlite3_column_text(pSelect, i)); - } - if( z==0 ) z = ""; - while( z[0] && (z[0]!='-' || z[1]!='-') ) z++; - if( z[0] ){ - raw_printf(p->out, "\n;\n"); - }else{ - raw_printf(p->out, ";\n"); - } - rc = sqlite3_step(pSelect); - } - rc = sqlite3_finalize(pSelect); - if( rc!=SQLITE_OK ){ - utf8_printf(p->out, "/**** ERROR: (%d) %s *****/\n", rc, - sqlite3_errmsg(p->db)); - if( (rc&0xff)!=SQLITE_CORRUPT ) p->nErr++; - } - return rc; -} - -/* -** Allocate space and save off current error string. -*/ -static char *save_err_msg( - sqlite3 *db /* Database to query */ -){ - int nErrMsg = 1+strlen30(sqlite3_errmsg(db)); - char *zErrMsg = sqlite3_malloc64(nErrMsg); - if( zErrMsg ){ - memcpy(zErrMsg, sqlite3_errmsg(db), nErrMsg); - } - return zErrMsg; -} - -#ifdef __linux__ -/* -** Attempt to display I/O stats on Linux using /proc/PID/io -*/ -static void displayLinuxIoStats(FILE *out){ - FILE *in; - char z[200]; - sqlite3_snprintf(sizeof(z), z, "/proc/%d/io", getpid()); - in = fopen(z, "rb"); - if( in==0 ) return; - while( fgets(z, sizeof(z), in)!=0 ){ - static const struct { - const char *zPattern; - const char *zDesc; - } aTrans[] = { - { "rchar: ", "Bytes received by read():" }, - { "wchar: ", "Bytes sent to write():" }, - { "syscr: ", "Read() system calls:" }, - { "syscw: ", "Write() system calls:" }, - { "read_bytes: ", "Bytes read from storage:" }, - { "write_bytes: ", "Bytes written to storage:" }, - { "cancelled_write_bytes: ", "Cancelled write bytes:" }, - }; - int i; - for(i=0; i1 ){ - sqlite3_snprintf(sizeof(zLine), zLine, zFormat, iCur, iHiwtr); - }else{ - sqlite3_snprintf(sizeof(zLine), zLine, zFormat, iHiwtr); - } - raw_printf(p->out, "%-36s %s\n", zLabel, zLine); -} - -/* -** Display memory stats. -*/ -static int display_stats( - sqlite3 *db, /* Database to query */ - ShellState *pArg, /* Pointer to ShellState */ - int bReset /* True to reset the stats */ -){ - int iCur; - int iHiwtr; - FILE *out; - if( pArg==0 || pArg->out==0 ) return 0; - out = pArg->out; - - if( pArg->pStmt && (pArg->statsOn & 2) ){ - int nCol, i, x; - sqlite3_stmt *pStmt = pArg->pStmt; - char z[100]; - nCol = sqlite3_column_count(pStmt); - raw_printf(out, "%-36s %d\n", "Number of output columns:", nCol); - for(i=0; ishellFlgs & SHFLG_Pagecache ){ - displayStatLine(pArg, "Number of Pcache Pages Used:", - "%lld (max %lld) pages", SQLITE_STATUS_PAGECACHE_USED, bReset); - } - displayStatLine(pArg, "Number of Pcache Overflow Bytes:", - "%lld (max %lld) bytes", SQLITE_STATUS_PAGECACHE_OVERFLOW, bReset); - displayStatLine(pArg, "Largest Allocation:", - "%lld bytes", SQLITE_STATUS_MALLOC_SIZE, bReset); - displayStatLine(pArg, "Largest Pcache Allocation:", - "%lld bytes", SQLITE_STATUS_PAGECACHE_SIZE, bReset); -#ifdef YYTRACKMAXSTACKDEPTH - displayStatLine(pArg, "Deepest Parser Stack:", - "%lld (max %lld)", SQLITE_STATUS_PARSER_STACK, bReset); -#endif - - if( db ){ - if( pArg->shellFlgs & SHFLG_Lookaside ){ - iHiwtr = iCur = -1; - sqlite3_db_status(db, SQLITE_DBSTATUS_LOOKASIDE_USED, - &iCur, &iHiwtr, bReset); - raw_printf(pArg->out, - "Lookaside Slots Used: %d (max %d)\n", - iCur, iHiwtr); - sqlite3_db_status(db, SQLITE_DBSTATUS_LOOKASIDE_HIT, - &iCur, &iHiwtr, bReset); - raw_printf(pArg->out, "Successful lookaside attempts: %d\n", - iHiwtr); - sqlite3_db_status(db, SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE, - &iCur, &iHiwtr, bReset); - raw_printf(pArg->out, "Lookaside failures due to size: %d\n", - iHiwtr); - sqlite3_db_status(db, SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL, - &iCur, &iHiwtr, bReset); - raw_printf(pArg->out, "Lookaside failures due to OOM: %d\n", - iHiwtr); - } - iHiwtr = iCur = -1; - sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_USED, &iCur, &iHiwtr, bReset); - raw_printf(pArg->out, "Pager Heap Usage: %d bytes\n", - iCur); - iHiwtr = iCur = -1; - sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_HIT, &iCur, &iHiwtr, 1); - raw_printf(pArg->out, "Page cache hits: %d\n", iCur); - iHiwtr = iCur = -1; - sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_MISS, &iCur, &iHiwtr, 1); - raw_printf(pArg->out, "Page cache misses: %d\n", iCur); - iHiwtr = iCur = -1; - sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_WRITE, &iCur, &iHiwtr, 1); - raw_printf(pArg->out, "Page cache writes: %d\n", iCur); - iHiwtr = iCur = -1; - sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_SPILL, &iCur, &iHiwtr, 1); - raw_printf(pArg->out, "Page cache spills: %d\n", iCur); - iHiwtr = iCur = -1; - sqlite3_db_status(db, SQLITE_DBSTATUS_SCHEMA_USED, &iCur, &iHiwtr, bReset); - raw_printf(pArg->out, "Schema Heap Usage: %d bytes\n", - iCur); - iHiwtr = iCur = -1; - sqlite3_db_status(db, SQLITE_DBSTATUS_STMT_USED, &iCur, &iHiwtr, bReset); - raw_printf(pArg->out, "Statement Heap/Lookaside Usage: %d bytes\n", - iCur); - } - - if( pArg->pStmt ){ - iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_FULLSCAN_STEP, - bReset); - raw_printf(pArg->out, "Fullscan Steps: %d\n", iCur); - iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_SORT, bReset); - raw_printf(pArg->out, "Sort Operations: %d\n", iCur); - iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_AUTOINDEX,bReset); - raw_printf(pArg->out, "Autoindex Inserts: %d\n", iCur); - iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_VM_STEP, bReset); - raw_printf(pArg->out, "Virtual Machine Steps: %d\n", iCur); - iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_REPREPARE,bReset); - raw_printf(pArg->out, "Reprepare operations: %d\n", iCur); - iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_RUN, bReset); - raw_printf(pArg->out, "Number of times run: %d\n", iCur); - iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_MEMUSED, bReset); - raw_printf(pArg->out, "Memory used by prepared stmt: %d\n", iCur); - } - -#ifdef __linux__ - displayLinuxIoStats(pArg->out); -#endif - - /* Do not remove this machine readable comment: extra-stats-output-here */ - - return 0; -} - -/* -** Display scan stats. -*/ -static void display_scanstats( - sqlite3 *db, /* Database to query */ - ShellState *pArg /* Pointer to ShellState */ -){ -#ifndef SQLITE_ENABLE_STMT_SCANSTATUS - UNUSED_PARAMETER(db); - UNUSED_PARAMETER(pArg); -#else - int i, k, n, mx; - raw_printf(pArg->out, "-------- scanstats --------\n"); - mx = 0; - for(k=0; k<=mx; k++){ - double rEstLoop = 1.0; - for(i=n=0; 1; i++){ - sqlite3_stmt *p = pArg->pStmt; - sqlite3_int64 nLoop, nVisit; - double rEst; - int iSid; - const char *zExplain; - if( sqlite3_stmt_scanstatus(p, i, SQLITE_SCANSTAT_NLOOP, (void*)&nLoop) ){ - break; - } - sqlite3_stmt_scanstatus(p, i, SQLITE_SCANSTAT_SELECTID, (void*)&iSid); - if( iSid>mx ) mx = iSid; - if( iSid!=k ) continue; - if( n==0 ){ - rEstLoop = (double)nLoop; - if( k>0 ) raw_printf(pArg->out, "-------- subquery %d -------\n", k); - } - n++; - sqlite3_stmt_scanstatus(p, i, SQLITE_SCANSTAT_NVISIT, (void*)&nVisit); - sqlite3_stmt_scanstatus(p, i, SQLITE_SCANSTAT_EST, (void*)&rEst); - sqlite3_stmt_scanstatus(p, i, SQLITE_SCANSTAT_EXPLAIN, (void*)&zExplain); - utf8_printf(pArg->out, "Loop %2d: %s\n", n, zExplain); - rEstLoop *= rEst; - raw_printf(pArg->out, - " nLoop=%-8lld nRow=%-8lld estRow=%-8lld estRow/Loop=%-8g\n", - nLoop, nVisit, (sqlite3_int64)(rEstLoop+0.5), rEst - ); - } - } - raw_printf(pArg->out, "---------------------------\n"); -#endif -} - -/* -** Parameter azArray points to a zero-terminated array of strings. zStr -** points to a single nul-terminated string. Return non-zero if zStr -** is equal, according to strcmp(), to any of the strings in the array. -** Otherwise, return zero. -*/ -static int str_in_array(const char *zStr, const char **azArray){ - int i; - for(i=0; azArray[i]; i++){ - if( 0==strcmp(zStr, azArray[i]) ) return 1; - } - return 0; -} - -/* -** If compiled statement pSql appears to be an EXPLAIN statement, allocate -** and populate the ShellState.aiIndent[] array with the number of -** spaces each opcode should be indented before it is output. -** -** The indenting rules are: -** -** * For each "Next", "Prev", "VNext" or "VPrev" instruction, indent -** all opcodes that occur between the p2 jump destination and the opcode -** itself by 2 spaces. -** -** * For each "Goto", if the jump destination is earlier in the program -** and ends on one of: -** Yield SeekGt SeekLt RowSetRead Rewind -** or if the P1 parameter is one instead of zero, -** then indent all opcodes between the earlier instruction -** and "Goto" by 2 spaces. -*/ -static void explain_data_prepare(ShellState *p, sqlite3_stmt *pSql){ - const char *zSql; /* The text of the SQL statement */ - const char *z; /* Used to check if this is an EXPLAIN */ - int *abYield = 0; /* True if op is an OP_Yield */ - int nAlloc = 0; /* Allocated size of p->aiIndent[], abYield */ - int iOp; /* Index of operation in p->aiIndent[] */ - - const char *azNext[] = { "Next", "Prev", "VPrev", "VNext", "SorterNext", 0 }; - const char *azYield[] = { "Yield", "SeekLT", "SeekGT", "RowSetRead", - "Rewind", 0 }; - const char *azGoto[] = { "Goto", 0 }; - - /* Try to figure out if this is really an EXPLAIN statement. If this - ** cannot be verified, return early. */ - if( sqlite3_column_count(pSql)!=8 ){ - p->cMode = p->mode; - return; - } - zSql = sqlite3_sql(pSql); - if( zSql==0 ) return; - for(z=zSql; *z==' ' || *z=='\t' || *z=='\n' || *z=='\f' || *z=='\r'; z++); - if( sqlite3_strnicmp(z, "explain", 7) ){ - p->cMode = p->mode; - return; - } - - for(iOp=0; SQLITE_ROW==sqlite3_step(pSql); iOp++){ - int i; - int iAddr = sqlite3_column_int(pSql, 0); - const char *zOp = (const char*)sqlite3_column_text(pSql, 1); - - /* Set p2 to the P2 field of the current opcode. Then, assuming that - ** p2 is an instruction address, set variable p2op to the index of that - ** instruction in the aiIndent[] array. p2 and p2op may be different if - ** the current instruction is part of a sub-program generated by an - ** SQL trigger or foreign key. */ - int p2 = sqlite3_column_int(pSql, 3); - int p2op = (p2 + (iOp-iAddr)); - - /* Grow the p->aiIndent array as required */ - if( iOp>=nAlloc ){ - if( iOp==0 ){ - /* Do further verfication that this is explain output. Abort if - ** it is not */ - static const char *explainCols[] = { - "addr", "opcode", "p1", "p2", "p3", "p4", "p5", "comment" }; - int jj; - for(jj=0; jjcMode = p->mode; - sqlite3_reset(pSql); - return; - } - } - } - nAlloc += 100; - p->aiIndent = (int*)sqlite3_realloc64(p->aiIndent, nAlloc*sizeof(int)); - if( p->aiIndent==0 ) shell_out_of_memory(); - abYield = (int*)sqlite3_realloc64(abYield, nAlloc*sizeof(int)); - if( abYield==0 ) shell_out_of_memory(); - } - abYield[iOp] = str_in_array(zOp, azYield); - p->aiIndent[iOp] = 0; - p->nIndent = iOp+1; - - if( str_in_array(zOp, azNext) ){ - for(i=p2op; iaiIndent[i] += 2; - } - if( str_in_array(zOp, azGoto) && p2opnIndent - && (abYield[p2op] || sqlite3_column_int(pSql, 2)) - ){ - for(i=p2op; iaiIndent[i] += 2; - } - } - - p->iIndent = 0; - sqlite3_free(abYield); - sqlite3_reset(pSql); -} - -/* -** Free the array allocated by explain_data_prepare(). -*/ -static void explain_data_delete(ShellState *p){ - sqlite3_free(p->aiIndent); - p->aiIndent = 0; - p->nIndent = 0; - p->iIndent = 0; -} - -/* -** Disable and restore .wheretrace and .selecttrace settings. -*/ -#if defined(SQLITE_DEBUG) && defined(SQLITE_ENABLE_SELECTTRACE) -extern unsigned int sqlite3_unsupported_selecttrace; -static int savedSelectTrace; -#endif -#if defined(SQLITE_DEBUG) && defined(SQLITE_ENABLE_WHERETRACE) -extern int sqlite3WhereTrace; -static int savedWhereTrace; -#endif -static void disable_debug_trace_modes(void){ -#if defined(SQLITE_DEBUG) && defined(SQLITE_ENABLE_SELECTTRACE) - savedSelectTrace = sqlite3_unsupported_selecttrace; - sqlite3_unsupported_selecttrace = 0; -#endif -#if defined(SQLITE_DEBUG) && defined(SQLITE_ENABLE_WHERETRACE) - savedWhereTrace = sqlite3WhereTrace; - sqlite3WhereTrace = 0; -#endif -} -static void restore_debug_trace_modes(void){ -#if defined(SQLITE_DEBUG) && defined(SQLITE_ENABLE_SELECTTRACE) - sqlite3_unsupported_selecttrace = savedSelectTrace; -#endif -#if defined(SQLITE_DEBUG) && defined(SQLITE_ENABLE_WHERETRACE) - sqlite3WhereTrace = savedWhereTrace; -#endif -} - -/* Create the TEMP table used to store parameter bindings */ -static void bind_table_init(ShellState *p){ - int wrSchema = 0; - int defensiveMode = 0; - sqlite3_db_config(p->db, SQLITE_DBCONFIG_DEFENSIVE, -1, &defensiveMode); - sqlite3_db_config(p->db, SQLITE_DBCONFIG_DEFENSIVE, 0, 0); - sqlite3_db_config(p->db, SQLITE_DBCONFIG_WRITABLE_SCHEMA, -1, &wrSchema); - sqlite3_db_config(p->db, SQLITE_DBCONFIG_WRITABLE_SCHEMA, 1, 0); - sqlite3_exec(p->db, - "CREATE TABLE IF NOT EXISTS temp.sqlite_parameters(\n" - " key TEXT PRIMARY KEY,\n" - " value ANY\n" - ") WITHOUT ROWID;", - 0, 0, 0); - sqlite3_db_config(p->db, SQLITE_DBCONFIG_WRITABLE_SCHEMA, wrSchema, 0); - sqlite3_db_config(p->db, SQLITE_DBCONFIG_DEFENSIVE, defensiveMode, 0); -} - -/* -** Bind parameters on a prepared statement. -** -** Parameter bindings are taken from a TEMP table of the form: -** -** CREATE TEMP TABLE sqlite_parameters(key TEXT PRIMARY KEY, value) -** WITHOUT ROWID; -** -** No bindings occur if this table does not exist. The name of the table -** begins with "sqlite_" so that it will not collide with ordinary application -** tables. The table must be in the TEMP schema. -*/ -static void bind_prepared_stmt(ShellState *pArg, sqlite3_stmt *pStmt){ - int nVar; - int i; - int rc; - sqlite3_stmt *pQ = 0; - - nVar = sqlite3_bind_parameter_count(pStmt); - if( nVar==0 ) return; /* Nothing to do */ - if( sqlite3_table_column_metadata(pArg->db, "TEMP", "sqlite_parameters", - "key", 0, 0, 0, 0, 0)!=SQLITE_OK ){ - return; /* Parameter table does not exist */ - } - rc = sqlite3_prepare_v2(pArg->db, - "SELECT value FROM temp.sqlite_parameters" - " WHERE key=?1", -1, &pQ, 0); - if( rc || pQ==0 ) return; - for(i=1; i<=nVar; i++){ - char zNum[30]; - const char *zVar = sqlite3_bind_parameter_name(pStmt, i); - if( zVar==0 ){ - sqlite3_snprintf(sizeof(zNum),zNum,"?%d",i); - zVar = zNum; - } - sqlite3_bind_text(pQ, 1, zVar, -1, SQLITE_STATIC); - if( sqlite3_step(pQ)==SQLITE_ROW ){ - sqlite3_bind_value(pStmt, i, sqlite3_column_value(pQ, 0)); - }else{ - sqlite3_bind_null(pStmt, i); - } - sqlite3_reset(pQ); - } - sqlite3_finalize(pQ); -} - -/* -** UTF8 box-drawing characters. Imagine box lines like this: -** -** 1 -** | -** 4 --+-- 2 -** | -** 3 -** -** Each box characters has between 2 and 4 of the lines leading from -** the center. The characters are here identified by the numbers of -** their corresponding lines. -*/ -#define BOX_24 "\342\224\200" /* U+2500 --- */ -#define BOX_13 "\342\224\202" /* U+2502 | */ -#define BOX_23 "\342\224\214" /* U+250c ,- */ -#define BOX_34 "\342\224\220" /* U+2510 -, */ -#define BOX_12 "\342\224\224" /* U+2514 '- */ -#define BOX_14 "\342\224\230" /* U+2518 -' */ -#define BOX_123 "\342\224\234" /* U+251c |- */ -#define BOX_134 "\342\224\244" /* U+2524 -| */ -#define BOX_234 "\342\224\254" /* U+252c -,- */ -#define BOX_124 "\342\224\264" /* U+2534 -'- */ -#define BOX_1234 "\342\224\274" /* U+253c -|- */ - -/* Draw horizontal line N characters long using unicode box -** characters -*/ -static void print_box_line(FILE *out, int N){ - const char zDash[] = - BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 - BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24; - const int nDash = sizeof(zDash) - 1; - N *= 3; - while( N>nDash ){ - utf8_printf(out, zDash); - N -= nDash; - } - utf8_printf(out, "%.*s", N, zDash); -} - -/* -** Draw a horizontal separator for a MODE_Box table. -*/ -static void print_box_row_separator( - ShellState *p, - int nArg, - const char *zSep1, - const char *zSep2, - const char *zSep3 -){ - int i; - if( nArg>0 ){ - utf8_printf(p->out, "%s", zSep1); - print_box_line(p->out, p->actualWidth[0]+2); - for(i=1; iout, "%s", zSep2); - print_box_line(p->out, p->actualWidth[i]+2); - } - utf8_printf(p->out, "%s", zSep3); - } - fputs("\n", p->out); -} - - - -/* -** Run a prepared statement and output the result in one of the -** table-oriented formats: MODE_Column, MODE_Markdown, MODE_Table, -** or MODE_Box. -** -** This is different from ordinary exec_prepared_stmt() in that -** it has to run the entire query and gather the results into memory -** first, in order to determine column widths, before providing -** any output. -*/ -static void exec_prepared_stmt_columnar( - ShellState *p, /* Pointer to ShellState */ - sqlite3_stmt *pStmt /* Statment to run */ -){ - sqlite3_int64 nRow = 0; - int nColumn = 0; - char **azData = 0; - sqlite3_int64 nAlloc = 0; - const char *z; - int rc; - sqlite3_int64 i, nData; - int j, nTotal, w, n; - const char *colSep = 0; - const char *rowSep = 0; - - rc = sqlite3_step(pStmt); - if( rc!=SQLITE_ROW ) return; - nColumn = sqlite3_column_count(pStmt); - nAlloc = nColumn*4; - azData = sqlite3_malloc64( nAlloc*sizeof(char*) ); - if( azData==0 ) shell_out_of_memory(); - for(i=0; i= nAlloc ){ - nAlloc *= 2; - azData = sqlite3_realloc64(azData, nAlloc*sizeof(char*)); - if( azData==0 ) shell_out_of_memory(); - } - nRow++; - for(i=0; ip->nWidth ){ - p->colWidth = realloc(p->colWidth, nColumn*2*sizeof(int)); - if( p->colWidth==0 ) shell_out_of_memory(); - for(i=p->nWidth; icolWidth[i] = 0; - p->nWidth = nColumn; - p->actualWidth = &p->colWidth[nColumn]; - } - memset(p->actualWidth, 0, nColumn*sizeof(int)); - for(i=0; icolWidth[i]; - if( w<0 ) w = -w; - p->actualWidth[i] = w; - } - nTotal = nColumn*(nRow+1); - for(i=0; inullValue; - n = strlenChar(z); - j = i%nColumn; - if( n>p->actualWidth[j] ) p->actualWidth[j] = n; - } - if( seenInterrupt ) goto columnar_end; - switch( p->cMode ){ - case MODE_Column: { - colSep = " "; - rowSep = "\n"; - if( p->showHeader ){ - for(i=0; iactualWidth[i]; - if( p->colWidth[i]<0 ) w = -w; - utf8_width_print(p->out, w, azData[i]); - fputs(i==nColumn-1?"\n":" ", p->out); - } - for(i=0; iout, p->actualWidth[i]); - fputs(i==nColumn-1?"\n":" ", p->out); - } - } - break; - } - case MODE_Table: { - colSep = " | "; - rowSep = " |\n"; - print_row_separator(p, nColumn, "+"); - fputs("| ", p->out); - for(i=0; iactualWidth[i]; - n = strlenChar(azData[i]); - utf8_printf(p->out, "%*s%s%*s", (w-n)/2, "", azData[i], (w-n+1)/2, ""); - fputs(i==nColumn-1?" |\n":" | ", p->out); - } - print_row_separator(p, nColumn, "+"); - break; - } - case MODE_Markdown: { - colSep = " | "; - rowSep = " |\n"; - fputs("| ", p->out); - for(i=0; iactualWidth[i]; - n = strlenChar(azData[i]); - utf8_printf(p->out, "%*s%s%*s", (w-n)/2, "", azData[i], (w-n+1)/2, ""); - fputs(i==nColumn-1?" |\n":" | ", p->out); - } - print_row_separator(p, nColumn, "|"); - break; - } - case MODE_Box: { - colSep = " " BOX_13 " "; - rowSep = " " BOX_13 "\n"; - print_box_row_separator(p, nColumn, BOX_23, BOX_234, BOX_34); - utf8_printf(p->out, BOX_13 " "); - for(i=0; iactualWidth[i]; - n = strlenChar(azData[i]); - utf8_printf(p->out, "%*s%s%*s%s", - (w-n)/2, "", azData[i], (w-n+1)/2, "", - i==nColumn-1?" "BOX_13"\n":" "BOX_13" "); - } - print_box_row_separator(p, nColumn, BOX_123, BOX_1234, BOX_134); - break; - } - } - for(i=nColumn, j=0; icMode!=MODE_Column ){ - utf8_printf(p->out, "%s", p->cMode==MODE_Box?BOX_13" ":"| "); - } - z = azData[i]; - if( z==0 ) z = p->nullValue; - w = p->actualWidth[j]; - if( p->colWidth[j]<0 ) w = -w; - utf8_width_print(p->out, w, z); - if( j==nColumn-1 ){ - utf8_printf(p->out, "%s", rowSep); - j = -1; - if( seenInterrupt ) goto columnar_end; - }else{ - utf8_printf(p->out, "%s", colSep); - } - } - if( p->cMode==MODE_Table ){ - print_row_separator(p, nColumn, "+"); - }else if( p->cMode==MODE_Box ){ - print_box_row_separator(p, nColumn, BOX_12, BOX_124, BOX_14); - } -columnar_end: - if( seenInterrupt ){ - utf8_printf(p->out, "Interrupt\n"); - } - nData = (nRow+1)*nColumn; - for(i=0; icMode==MODE_Column - || pArg->cMode==MODE_Table - || pArg->cMode==MODE_Box - || pArg->cMode==MODE_Markdown - ){ - exec_prepared_stmt_columnar(pArg, pStmt); - return; - } - - /* perform the first step. this will tell us if we - ** have a result set or not and how wide it is. - */ - rc = sqlite3_step(pStmt); - /* if we have a result set... */ - if( SQLITE_ROW == rc ){ - /* allocate space for col name ptr, value ptr, and type */ - int nCol = sqlite3_column_count(pStmt); - void *pData = sqlite3_malloc64(3*nCol*sizeof(const char*) + 1); - if( !pData ){ - rc = SQLITE_NOMEM; - }else{ - char **azCols = (char **)pData; /* Names of result columns */ - char **azVals = &azCols[nCol]; /* Results */ - int *aiTypes = (int *)&azVals[nCol]; /* Result types */ - int i, x; - assert(sizeof(int) <= sizeof(char *)); - /* save off ptrs to column names */ - for(i=0; icMode==MODE_Insert ){ - azVals[i] = ""; - }else{ - azVals[i] = (char*)sqlite3_column_text(pStmt, i); - } - if( !azVals[i] && (aiTypes[i]!=SQLITE_NULL) ){ - rc = SQLITE_NOMEM; - break; /* from for */ - } - } /* end for */ - - /* if data and types extracted successfully... */ - if( SQLITE_ROW == rc ){ - /* call the supplied callback with the result row data */ - if( shell_callback(pArg, nCol, azVals, azCols, aiTypes) ){ - rc = SQLITE_ABORT; - }else{ - rc = sqlite3_step(pStmt); - } - } - } while( SQLITE_ROW == rc ); - sqlite3_free(pData); - if( pArg->cMode==MODE_Json ){ - fputs("]\n", pArg->out); - } - } - } -} - -#ifndef SQLITE_OMIT_VIRTUALTABLE -/* -** This function is called to process SQL if the previous shell command -** was ".expert". It passes the SQL in the second argument directly to -** the sqlite3expert object. -** -** If successful, SQLITE_OK is returned. Otherwise, an SQLite error -** code. In this case, (*pzErr) may be set to point to a buffer containing -** an English language error message. It is the responsibility of the -** caller to eventually free this buffer using sqlite3_free(). -*/ -static int expertHandleSQL( - ShellState *pState, - const char *zSql, - char **pzErr -){ - assert( pState->expert.pExpert ); - assert( pzErr==0 || *pzErr==0 ); - return sqlite3_expert_sql(pState->expert.pExpert, zSql, pzErr); -} - -/* -** This function is called either to silently clean up the object -** created by the ".expert" command (if bCancel==1), or to generate a -** report from it and then clean it up (if bCancel==0). -** -** If successful, SQLITE_OK is returned. Otherwise, an SQLite error -** code. In this case, (*pzErr) may be set to point to a buffer containing -** an English language error message. It is the responsibility of the -** caller to eventually free this buffer using sqlite3_free(). -*/ -static int expertFinish( - ShellState *pState, - int bCancel, - char **pzErr -){ - int rc = SQLITE_OK; - sqlite3expert *p = pState->expert.pExpert; - assert( p ); - assert( bCancel || pzErr==0 || *pzErr==0 ); - if( bCancel==0 ){ - FILE *out = pState->out; - int bVerbose = pState->expert.bVerbose; - - rc = sqlite3_expert_analyze(p, pzErr); - if( rc==SQLITE_OK ){ - int nQuery = sqlite3_expert_count(p); - int i; - - if( bVerbose ){ - const char *zCand = sqlite3_expert_report(p,0,EXPERT_REPORT_CANDIDATES); - raw_printf(out, "-- Candidates -----------------------------\n"); - raw_printf(out, "%s\n", zCand); - } - for(i=0; iexpert.pExpert = 0; - return rc; -} - -/* -** Implementation of ".expert" dot command. -*/ -static int expertDotCommand( - ShellState *pState, /* Current shell tool state */ - char **azArg, /* Array of arguments passed to dot command */ - int nArg /* Number of entries in azArg[] */ -){ - int rc = SQLITE_OK; - char *zErr = 0; - int i; - int iSample = 0; - - assert( pState->expert.pExpert==0 ); - memset(&pState->expert, 0, sizeof(ExpertInfo)); - - for(i=1; rc==SQLITE_OK && i=2 && 0==strncmp(z, "-verbose", n) ){ - pState->expert.bVerbose = 1; - } - else if( n>=2 && 0==strncmp(z, "-sample", n) ){ - if( i==(nArg-1) ){ - raw_printf(stderr, "option requires an argument: %s\n", z); - rc = SQLITE_ERROR; - }else{ - iSample = (int)integerValue(azArg[++i]); - if( iSample<0 || iSample>100 ){ - raw_printf(stderr, "value out of range: %s\n", azArg[i]); - rc = SQLITE_ERROR; - } - } - } - else{ - raw_printf(stderr, "unknown option: %s\n", z); - rc = SQLITE_ERROR; - } - } - - if( rc==SQLITE_OK ){ - pState->expert.pExpert = sqlite3_expert_new(pState->db, &zErr); - if( pState->expert.pExpert==0 ){ - raw_printf(stderr, "sqlite3_expert_new: %s\n", zErr); - rc = SQLITE_ERROR; - }else{ - sqlite3_expert_config( - pState->expert.pExpert, EXPERT_CONFIG_SAMPLE, iSample - ); - } - } - - return rc; -} -#endif /* ifndef SQLITE_OMIT_VIRTUALTABLE */ - -/* -** Execute a statement or set of statements. Print -** any result rows/columns depending on the current mode -** set via the supplied callback. -** -** This is very similar to SQLite's built-in sqlite3_exec() -** function except it takes a slightly different callback -** and callback data argument. -*/ -static int shell_exec( - ShellState *pArg, /* Pointer to ShellState */ - const char *zSql, /* SQL to be evaluated */ - char **pzErrMsg /* Error msg written here */ -){ - sqlite3_stmt *pStmt = NULL; /* Statement to execute. */ - int rc = SQLITE_OK; /* Return Code */ - int rc2; - const char *zLeftover; /* Tail of unprocessed SQL */ - sqlite3 *db = pArg->db; - - if( pzErrMsg ){ - *pzErrMsg = NULL; - } - -#ifndef SQLITE_OMIT_VIRTUALTABLE - if( pArg->expert.pExpert ){ - rc = expertHandleSQL(pArg, zSql, pzErrMsg); - return expertFinish(pArg, (rc!=SQLITE_OK), pzErrMsg); - } -#endif - - while( zSql[0] && (SQLITE_OK == rc) ){ - static const char *zStmtSql; - rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, &zLeftover); - if( SQLITE_OK != rc ){ - if( pzErrMsg ){ - *pzErrMsg = save_err_msg(db); - } - }else{ - if( !pStmt ){ - /* this happens for a comment or white-space */ - zSql = zLeftover; - while( IsSpace(zSql[0]) ) zSql++; - continue; - } - zStmtSql = sqlite3_sql(pStmt); - if( zStmtSql==0 ) zStmtSql = ""; - while( IsSpace(zStmtSql[0]) ) zStmtSql++; - - /* save off the prepared statment handle and reset row count */ - if( pArg ){ - pArg->pStmt = pStmt; - pArg->cnt = 0; - } - - /* echo the sql statement if echo on */ - if( pArg && ShellHasFlag(pArg, SHFLG_Echo) ){ - utf8_printf(pArg->out, "%s\n", zStmtSql ? zStmtSql : zSql); - } - - /* Show the EXPLAIN QUERY PLAN if .eqp is on */ - if( pArg && pArg->autoEQP && sqlite3_stmt_isexplain(pStmt)==0 ){ - sqlite3_stmt *pExplain; - char *zEQP; - int triggerEQP = 0; - disable_debug_trace_modes(); - sqlite3_db_config(db, SQLITE_DBCONFIG_TRIGGER_EQP, -1, &triggerEQP); - if( pArg->autoEQP>=AUTOEQP_trigger ){ - sqlite3_db_config(db, SQLITE_DBCONFIG_TRIGGER_EQP, 1, 0); - } - zEQP = sqlite3_mprintf("EXPLAIN QUERY PLAN %s", zStmtSql); - rc = sqlite3_prepare_v2(db, zEQP, -1, &pExplain, 0); - if( rc==SQLITE_OK ){ - while( sqlite3_step(pExplain)==SQLITE_ROW ){ - const char *zEQPLine = (const char*)sqlite3_column_text(pExplain,3); - int iEqpId = sqlite3_column_int(pExplain, 0); - int iParentId = sqlite3_column_int(pExplain, 1); - if( zEQPLine==0 ) zEQPLine = ""; - if( zEQPLine[0]=='-' ) eqp_render(pArg); - eqp_append(pArg, iEqpId, iParentId, zEQPLine); - } - eqp_render(pArg); - } - sqlite3_finalize(pExplain); - sqlite3_free(zEQP); - if( pArg->autoEQP>=AUTOEQP_full ){ - /* Also do an EXPLAIN for ".eqp full" mode */ - zEQP = sqlite3_mprintf("EXPLAIN %s", zStmtSql); - rc = sqlite3_prepare_v2(db, zEQP, -1, &pExplain, 0); - if( rc==SQLITE_OK ){ - pArg->cMode = MODE_Explain; - explain_data_prepare(pArg, pExplain); - exec_prepared_stmt(pArg, pExplain); - explain_data_delete(pArg); - } - sqlite3_finalize(pExplain); - sqlite3_free(zEQP); - } - if( pArg->autoEQP>=AUTOEQP_trigger && triggerEQP==0 ){ - sqlite3_db_config(db, SQLITE_DBCONFIG_TRIGGER_EQP, 0, 0); - /* Reprepare pStmt before reactiving trace modes */ - sqlite3_finalize(pStmt); - sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0); - if( pArg ) pArg->pStmt = pStmt; - } - restore_debug_trace_modes(); - } - - if( pArg ){ - pArg->cMode = pArg->mode; - if( pArg->autoExplain ){ - if( sqlite3_stmt_isexplain(pStmt)==1 ){ - pArg->cMode = MODE_Explain; - } - if( sqlite3_stmt_isexplain(pStmt)==2 ){ - pArg->cMode = MODE_EQP; - } - } - - /* If the shell is currently in ".explain" mode, gather the extra - ** data required to add indents to the output.*/ - if( pArg->cMode==MODE_Explain ){ - explain_data_prepare(pArg, pStmt); - } - } - - bind_prepared_stmt(pArg, pStmt); - exec_prepared_stmt(pArg, pStmt); - explain_data_delete(pArg); - eqp_render(pArg); - - /* print usage stats if stats on */ - if( pArg && pArg->statsOn ){ - display_stats(db, pArg, 0); - } - - /* print loop-counters if required */ - if( pArg && pArg->scanstatsOn ){ - display_scanstats(db, pArg); - } - - /* Finalize the statement just executed. If this fails, save a - ** copy of the error message. Otherwise, set zSql to point to the - ** next statement to execute. */ - rc2 = sqlite3_finalize(pStmt); - if( rc!=SQLITE_NOMEM ) rc = rc2; - if( rc==SQLITE_OK ){ - zSql = zLeftover; - while( IsSpace(zSql[0]) ) zSql++; - }else if( pzErrMsg ){ - *pzErrMsg = save_err_msg(db); - } - - /* clear saved stmt handle */ - if( pArg ){ - pArg->pStmt = NULL; - } - } - } /* end while */ - - return rc; -} - -/* -** Release memory previously allocated by tableColumnList(). -*/ -static void freeColumnList(char **azCol){ - int i; - for(i=1; azCol[i]; i++){ - sqlite3_free(azCol[i]); - } - /* azCol[0] is a static string */ - sqlite3_free(azCol); -} - -/* -** Return a list of pointers to strings which are the names of all -** columns in table zTab. The memory to hold the names is dynamically -** allocated and must be released by the caller using a subsequent call -** to freeColumnList(). -** -** The azCol[0] entry is usually NULL. However, if zTab contains a rowid -** value that needs to be preserved, then azCol[0] is filled in with the -** name of the rowid column. -** -** The first regular column in the table is azCol[1]. The list is terminated -** by an entry with azCol[i]==0. -*/ -static char **tableColumnList(ShellState *p, const char *zTab){ - char **azCol = 0; - sqlite3_stmt *pStmt; - char *zSql; - int nCol = 0; - int nAlloc = 0; - int nPK = 0; /* Number of PRIMARY KEY columns seen */ - int isIPK = 0; /* True if one PRIMARY KEY column of type INTEGER */ - int preserveRowid = ShellHasFlag(p, SHFLG_PreserveRowid); - int rc; - - zSql = sqlite3_mprintf("PRAGMA table_info=%Q", zTab); - rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0); - sqlite3_free(zSql); - if( rc ) return 0; - while( sqlite3_step(pStmt)==SQLITE_ROW ){ - if( nCol>=nAlloc-2 ){ - nAlloc = nAlloc*2 + nCol + 10; - azCol = sqlite3_realloc(azCol, nAlloc*sizeof(azCol[0])); - if( azCol==0 ) shell_out_of_memory(); - } - azCol[++nCol] = sqlite3_mprintf("%s", sqlite3_column_text(pStmt, 1)); - if( sqlite3_column_int(pStmt, 5) ){ - nPK++; - if( nPK==1 - && sqlite3_stricmp((const char*)sqlite3_column_text(pStmt,2), - "INTEGER")==0 - ){ - isIPK = 1; - }else{ - isIPK = 0; - } - } - } - sqlite3_finalize(pStmt); - if( azCol==0 ) return 0; - azCol[0] = 0; - azCol[nCol+1] = 0; - - /* The decision of whether or not a rowid really needs to be preserved - ** is tricky. We never need to preserve a rowid for a WITHOUT ROWID table - ** or a table with an INTEGER PRIMARY KEY. We are unable to preserve - ** rowids on tables where the rowid is inaccessible because there are other - ** columns in the table named "rowid", "_rowid_", and "oid". - */ - if( preserveRowid && isIPK ){ - /* If a single PRIMARY KEY column with type INTEGER was seen, then it - ** might be an alise for the ROWID. But it might also be a WITHOUT ROWID - ** table or a INTEGER PRIMARY KEY DESC column, neither of which are - ** ROWID aliases. To distinguish these cases, check to see if - ** there is a "pk" entry in "PRAGMA index_list". There will be - ** no "pk" index if the PRIMARY KEY really is an alias for the ROWID. - */ - zSql = sqlite3_mprintf("SELECT 1 FROM pragma_index_list(%Q)" - " WHERE origin='pk'", zTab); - rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0); - sqlite3_free(zSql); - if( rc ){ - freeColumnList(azCol); - return 0; - } - rc = sqlite3_step(pStmt); - sqlite3_finalize(pStmt); - preserveRowid = rc==SQLITE_ROW; - } - if( preserveRowid ){ - /* Only preserve the rowid if we can find a name to use for the - ** rowid */ - static char *azRowid[] = { "rowid", "_rowid_", "oid" }; - int i, j; - for(j=0; j<3; j++){ - for(i=1; i<=nCol; i++){ - if( sqlite3_stricmp(azRowid[j],azCol[i])==0 ) break; - } - if( i>nCol ){ - /* At this point, we know that azRowid[j] is not the name of any - ** ordinary column in the table. Verify that azRowid[j] is a valid - ** name for the rowid before adding it to azCol[0]. WITHOUT ROWID - ** tables will fail this last check */ - rc = sqlite3_table_column_metadata(p->db,0,zTab,azRowid[j],0,0,0,0,0); - if( rc==SQLITE_OK ) azCol[0] = azRowid[j]; - break; - } - } - } - return azCol; -} - -/* -** Toggle the reverse_unordered_selects setting. -*/ -static void toggleSelectOrder(sqlite3 *db){ - sqlite3_stmt *pStmt = 0; - int iSetting = 0; - char zStmt[100]; - sqlite3_prepare_v2(db, "PRAGMA reverse_unordered_selects", -1, &pStmt, 0); - if( sqlite3_step(pStmt)==SQLITE_ROW ){ - iSetting = sqlite3_column_int(pStmt, 0); - } - sqlite3_finalize(pStmt); - sqlite3_snprintf(sizeof(zStmt), zStmt, - "PRAGMA reverse_unordered_selects(%d)", !iSetting); - sqlite3_exec(db, zStmt, 0, 0, 0); -} - -/* -** This is a different callback routine used for dumping the database. -** Each row received by this callback consists of a table name, -** the table type ("index" or "table") and SQL to create the table. -** This routine should print text sufficient to recreate the table. -*/ -static int dump_callback(void *pArg, int nArg, char **azArg, char **azNotUsed){ - int rc; - const char *zTable; - const char *zType; - const char *zSql; - ShellState *p = (ShellState *)pArg; - - UNUSED_PARAMETER(azNotUsed); - if( nArg!=3 || azArg==0 ) return 0; - zTable = azArg[0]; - zType = azArg[1]; - zSql = azArg[2]; - - if( strcmp(zTable, "sqlite_sequence")==0 ){ - raw_printf(p->out, "DELETE FROM sqlite_sequence;\n"); - }else if( sqlite3_strglob("sqlite_stat?", zTable)==0 ){ - raw_printf(p->out, "ANALYZE sqlite_schema;\n"); - }else if( strncmp(zTable, "sqlite_", 7)==0 ){ - return 0; - }else if( strncmp(zSql, "CREATE VIRTUAL TABLE", 20)==0 ){ - char *zIns; - if( !p->writableSchema ){ - raw_printf(p->out, "PRAGMA writable_schema=ON;\n"); - p->writableSchema = 1; - } - zIns = sqlite3_mprintf( - "INSERT INTO sqlite_schema(type,name,tbl_name,rootpage,sql)" - "VALUES('table','%q','%q',0,'%q');", - zTable, zTable, zSql); - utf8_printf(p->out, "%s\n", zIns); - sqlite3_free(zIns); - return 0; - }else{ - printSchemaLine(p->out, zSql, ";\n"); - } - - if( strcmp(zType, "table")==0 ){ - ShellText sSelect; - ShellText sTable; - char **azCol; - int i; - char *savedDestTable; - int savedMode; - - azCol = tableColumnList(p, zTable); - if( azCol==0 ){ - p->nErr++; - return 0; - } - - /* Always quote the table name, even if it appears to be pure ascii, - ** in case it is a keyword. Ex: INSERT INTO "table" ... */ - initText(&sTable); - appendText(&sTable, zTable, quoteChar(zTable)); - /* If preserving the rowid, add a column list after the table name. - ** In other words: "INSERT INTO tab(rowid,a,b,c,...) VALUES(...)" - ** instead of the usual "INSERT INTO tab VALUES(...)". - */ - if( azCol[0] ){ - appendText(&sTable, "(", 0); - appendText(&sTable, azCol[0], 0); - for(i=1; azCol[i]; i++){ - appendText(&sTable, ",", 0); - appendText(&sTable, azCol[i], quoteChar(azCol[i])); - } - appendText(&sTable, ")", 0); - } - - /* Build an appropriate SELECT statement */ - initText(&sSelect); - appendText(&sSelect, "SELECT ", 0); - if( azCol[0] ){ - appendText(&sSelect, azCol[0], 0); - appendText(&sSelect, ",", 0); - } - for(i=1; azCol[i]; i++){ - appendText(&sSelect, azCol[i], quoteChar(azCol[i])); - if( azCol[i+1] ){ - appendText(&sSelect, ",", 0); - } - } - freeColumnList(azCol); - appendText(&sSelect, " FROM ", 0); - appendText(&sSelect, zTable, quoteChar(zTable)); - - savedDestTable = p->zDestTable; - savedMode = p->mode; - p->zDestTable = sTable.z; - p->mode = p->cMode = MODE_Insert; - rc = shell_exec(p, sSelect.z, 0); - if( (rc&0xff)==SQLITE_CORRUPT ){ - raw_printf(p->out, "/****** CORRUPTION ERROR *******/\n"); - toggleSelectOrder(p->db); - shell_exec(p, sSelect.z, 0); - toggleSelectOrder(p->db); - } - p->zDestTable = savedDestTable; - p->mode = savedMode; - freeText(&sTable); - freeText(&sSelect); - if( rc ) p->nErr++; - } - return 0; -} - -/* -** Run zQuery. Use dump_callback() as the callback routine so that -** the contents of the query are output as SQL statements. -** -** If we get a SQLITE_CORRUPT error, rerun the query after appending -** "ORDER BY rowid DESC" to the end. -*/ -static int run_schema_dump_query( - ShellState *p, - const char *zQuery -){ - int rc; - char *zErr = 0; - rc = sqlite3_exec(p->db, zQuery, dump_callback, p, &zErr); - if( rc==SQLITE_CORRUPT ){ - char *zQ2; - int len = strlen30(zQuery); - raw_printf(p->out, "/****** CORRUPTION ERROR *******/\n"); - if( zErr ){ - utf8_printf(p->out, "/****** %s ******/\n", zErr); - sqlite3_free(zErr); - zErr = 0; - } - zQ2 = malloc( len+100 ); - if( zQ2==0 ) return rc; - sqlite3_snprintf(len+100, zQ2, "%s ORDER BY rowid DESC", zQuery); - rc = sqlite3_exec(p->db, zQ2, dump_callback, p, &zErr); - if( rc ){ - utf8_printf(p->out, "/****** ERROR: %s ******/\n", zErr); - }else{ - rc = SQLITE_CORRUPT; - } - sqlite3_free(zErr); - free(zQ2); - } - return rc; -} - -/* -** Text of help messages. -** -** The help text for each individual command begins with a line that starts -** with ".". Subsequent lines are supplimental information. -** -** There must be two or more spaces between the end of the command and the -** start of the description of what that command does. -*/ -static const char *(azHelp[]) = { -#if defined(SQLITE_HAVE_ZLIB) && !defined(SQLITE_OMIT_VIRTUALTABLE) - ".archive ... Manage SQL archives", - " Each command must have exactly one of the following options:", - " -c, --create Create a new archive", - " -u, --update Add or update files with changed mtime", - " -i, --insert Like -u but always add even if unchanged", - " -t, --list List contents of archive", - " -x, --extract Extract files from archive", - " Optional arguments:", - " -v, --verbose Print each filename as it is processed", - " -f FILE, --file FILE Use archive FILE (default is current db)", - " -a FILE, --append FILE Open FILE using the apndvfs VFS", - " -C DIR, --directory DIR Read/extract files from directory DIR", - " -n, --dryrun Show the SQL that would have occurred", - " Examples:", - " .ar -cf ARCHIVE foo bar # Create ARCHIVE from files foo and bar", - " .ar -tf ARCHIVE # List members of ARCHIVE", - " .ar -xvf ARCHIVE # Verbosely extract files from ARCHIVE", - " See also:", - " http://sqlite.org/cli.html#sqlar_archive_support", -#endif -#ifndef SQLITE_OMIT_AUTHORIZATION - ".auth ON|OFF Show authorizer callbacks", -#endif - ".backup ?DB? FILE Backup DB (default \"main\") to FILE", - " --append Use the appendvfs", - " --async Write to FILE without journal and fsync()", - ".bail on|off Stop after hitting an error. Default OFF", - ".binary on|off Turn binary output on or off. Default OFF", - ".cd DIRECTORY Change the working directory to DIRECTORY", - ".changes on|off Show number of rows changed by SQL", - ".check GLOB Fail if output since .testcase does not match", - ".clone NEWDB Clone data into NEWDB from the existing database", - ".databases List names and files of attached databases", - ".dbconfig ?op? ?val? List or change sqlite3_db_config() options", - ".dbinfo ?DB? Show status information about the database", - ".dump ?TABLE? Render database content as SQL", - " Options:", - " --preserve-rowids Include ROWID values in the output", - " --newlines Allow unescaped newline characters in output", - " TABLE is a LIKE pattern for the tables to dump", - " Additional LIKE patterns can be given in subsequent arguments", - ".echo on|off Turn command echo on or off", - ".eqp on|off|full|... Enable or disable automatic EXPLAIN QUERY PLAN", - " Other Modes:", -#ifdef SQLITE_DEBUG - " test Show raw EXPLAIN QUERY PLAN output", - " trace Like \"full\" but enable \"PRAGMA vdbe_trace\"", -#endif - " trigger Like \"full\" but also show trigger bytecode", - ".excel Display the output of next command in spreadsheet", - " --bom Put a UTF8 byte-order mark on intermediate file", - ".exit ?CODE? Exit this program with return-code CODE", - ".expert EXPERIMENTAL. Suggest indexes for queries", - ".explain ?on|off|auto? Change the EXPLAIN formatting mode. Default: auto", - ".filectrl CMD ... Run various sqlite3_file_control() operations", - " --schema SCHEMA Use SCHEMA instead of \"main\"", - " --help Show CMD details", - ".fullschema ?--indent? Show schema and the content of sqlite_stat tables", - ".headers on|off Turn display of headers on or off", - ".help ?-all? ?PATTERN? Show help text for PATTERN", - ".import FILE TABLE Import data from FILE into TABLE", - " Options:", - " --ascii Use \\037 and \\036 as column and row separators", - " --csv Use , and \\n as column and row separators", - " --skip N Skip the first N rows of input", - " -v \"Verbose\" - increase auxiliary output", - " Notes:", - " * If TABLE does not exist, it is created. The first row of input", - " determines the column names.", - " * If neither --csv or --ascii are used, the input mode is derived", - " from the \".mode\" output mode", - " * If FILE begins with \"|\" then it is a command that generates the", - " input text.", -#ifndef SQLITE_OMIT_TEST_CONTROL - ".imposter INDEX TABLE Create imposter table TABLE on index INDEX", -#endif - ".indexes ?TABLE? Show names of indexes", - " If TABLE is specified, only show indexes for", - " tables matching TABLE using the LIKE operator.", -#ifdef SQLITE_ENABLE_IOTRACE - ".iotrace FILE Enable I/O diagnostic logging to FILE", -#endif - ".limit ?LIMIT? ?VAL? Display or change the value of an SQLITE_LIMIT", - ".lint OPTIONS Report potential schema issues.", - " Options:", - " fkey-indexes Find missing foreign key indexes", -#ifndef SQLITE_OMIT_LOAD_EXTENSION - ".load FILE ?ENTRY? Load an extension library", -#endif - ".log FILE|off Turn logging on or off. FILE can be stderr/stdout", - ".mode MODE ?TABLE? Set output mode", - " MODE is one of:", - " ascii Columns/rows delimited by 0x1F and 0x1E", - " box Tables using unicode box-drawing characters", - " csv Comma-separated values", - " column Output in columns. (See .width)", - " html HTML code", - " insert SQL insert statements for TABLE", - " json Results in a JSON array", - " line One value per line", - " list Values delimited by \"|\"", - " markdown Markdown table format", - " quote Escape answers as for SQL", - " table ASCII-art table", - " tabs Tab-separated values", - " tcl TCL list elements", - ".nullvalue STRING Use STRING in place of NULL values", - ".once ?OPTIONS? ?FILE? Output for the next SQL command only to FILE", - " If FILE begins with '|' then open as a pipe", - " --bom Put a UTF8 byte-order mark at the beginning", - " -e Send output to the system text editor", - " -x Send output as CSV to a spreadsheet (same as \".excel\")", -#ifdef SQLITE_DEBUG - ".oom ?--repeat M? ?N? Simulate an OOM error on the N-th allocation", -#endif - ".open ?OPTIONS? ?FILE? Close existing database and reopen FILE", - " Options:", - " --append Use appendvfs to append database to the end of FILE", -#ifdef SQLITE_ENABLE_DESERIALIZE - " --deserialize Load into memory useing sqlite3_deserialize()", - " --hexdb Load the output of \"dbtotxt\" as an in-memory db", - " --maxsize N Maximum size for --hexdb or --deserialized database", -#endif - " --new Initialize FILE to an empty database", - " --nofollow Do not follow symbolic links", - " --readonly Open FILE readonly", - " --zip FILE is a ZIP archive", - ".output ?FILE? Send output to FILE or stdout if FILE is omitted", - " If FILE begins with '|' then open it as a pipe.", - " Options:", - " --bom Prefix output with a UTF8 byte-order mark", - " -e Send output to the system text editor", - " -x Send output as CSV to a spreadsheet", - ".parameter CMD ... Manage SQL parameter bindings", - " clear Erase all bindings", - " init Initialize the TEMP table that holds bindings", - " list List the current parameter bindings", - " set PARAMETER VALUE Given SQL parameter PARAMETER a value of VALUE", - " PARAMETER should start with one of: $ : @ ?", - " unset PARAMETER Remove PARAMETER from the binding table", - ".print STRING... Print literal STRING", -#ifndef SQLITE_OMIT_PROGRESS_CALLBACK - ".progress N Invoke progress handler after every N opcodes", - " --limit N Interrupt after N progress callbacks", - " --once Do no more than one progress interrupt", - " --quiet|-q No output except at interrupts", - " --reset Reset the count for each input and interrupt", -#endif - ".prompt MAIN CONTINUE Replace the standard prompts", - ".quit Exit this program", - ".read FILE Read input from FILE", -#if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_ENABLE_DBPAGE_VTAB) - ".recover Recover as much data as possible from corrupt db.", - " --freelist-corrupt Assume the freelist is corrupt", - " --recovery-db NAME Store recovery metadata in database file NAME", - " --lost-and-found TABLE Alternative name for the lost-and-found table", - " --no-rowids Do not attempt to recover rowid values", - " that are not also INTEGER PRIMARY KEYs", -#endif - ".restore ?DB? FILE Restore content of DB (default \"main\") from FILE", - ".save FILE Write in-memory database into FILE", - ".scanstats on|off Turn sqlite3_stmt_scanstatus() metrics on or off", - ".schema ?PATTERN? Show the CREATE statements matching PATTERN", - " Options:", - " --indent Try to pretty-print the schema", - ".selftest ?OPTIONS? Run tests defined in the SELFTEST table", - " Options:", - " --init Create a new SELFTEST table", - " -v Verbose output", - ".separator COL ?ROW? Change the column and row separators", -#if defined(SQLITE_ENABLE_SESSION) - ".session ?NAME? CMD ... Create or control sessions", - " Subcommands:", - " attach TABLE Attach TABLE", - " changeset FILE Write a changeset into FILE", - " close Close one session", - " enable ?BOOLEAN? Set or query the enable bit", - " filter GLOB... Reject tables matching GLOBs", - " indirect ?BOOLEAN? Mark or query the indirect status", - " isempty Query whether the session is empty", - " list List currently open session names", - " open DB NAME Open a new session on DB", - " patchset FILE Write a patchset into FILE", - " If ?NAME? is omitted, the first defined session is used.", -#endif - ".sha3sum ... Compute a SHA3 hash of database content", - " Options:", - " --schema Also hash the sqlite_schema table", - " --sha3-224 Use the sha3-224 algorithm", - " --sha3-256 Use the sha3-256 algorithm (default)", - " --sha3-384 Use the sha3-384 algorithm", - " --sha3-512 Use the sha3-512 algorithm", - " Any other argument is a LIKE pattern for tables to hash", -#ifndef SQLITE_NOHAVE_SYSTEM - ".shell CMD ARGS... Run CMD ARGS... in a system shell", -#endif - ".show Show the current values for various settings", - ".stats ?on|off? Show stats or turn stats on or off", -#ifndef SQLITE_NOHAVE_SYSTEM - ".system CMD ARGS... Run CMD ARGS... in a system shell", -#endif - ".tables ?TABLE? List names of tables matching LIKE pattern TABLE", - ".testcase NAME Begin redirecting output to 'testcase-out.txt'", - ".testctrl CMD ... Run various sqlite3_test_control() operations", - " Run \".testctrl\" with no arguments for details", - ".timeout MS Try opening locked tables for MS milliseconds", - ".timer on|off Turn SQL timer on or off", -#ifndef SQLITE_OMIT_TRACE - ".trace ?OPTIONS? Output each SQL statement as it is run", - " FILE Send output to FILE", - " stdout Send output to stdout", - " stderr Send output to stderr", - " off Disable tracing", - " --expanded Expand query parameters", -#ifdef SQLITE_ENABLE_NORMALIZE - " --normalized Normal the SQL statements", -#endif - " --plain Show SQL as it is input", - " --stmt Trace statement execution (SQLITE_TRACE_STMT)", - " --profile Profile statements (SQLITE_TRACE_PROFILE)", - " --row Trace each row (SQLITE_TRACE_ROW)", - " --close Trace connection close (SQLITE_TRACE_CLOSE)", -#endif /* SQLITE_OMIT_TRACE */ -#ifdef SQLITE_DEBUG - ".unmodule NAME ... Unregister virtual table modules", - " --allexcept Unregister everything except those named", -#endif - ".vfsinfo ?AUX? Information about the top-level VFS", - ".vfslist List all available VFSes", - ".vfsname ?AUX? Print the name of the VFS stack", - ".width NUM1 NUM2 ... Set minimum column widths for columnar output", - " Negative values right-justify", -}; - -/* -** Output help text. -** -** zPattern describes the set of commands for which help text is provided. -** If zPattern is NULL, then show all commands, but only give a one-line -** description of each. -** -** Return the number of matches. -*/ -static int showHelp(FILE *out, const char *zPattern){ - int i = 0; - int j = 0; - int n = 0; - char *zPat; - if( zPattern==0 - || zPattern[0]=='0' - || strcmp(zPattern,"-a")==0 - || strcmp(zPattern,"-all")==0 - || strcmp(zPattern,"--all")==0 - ){ - /* Show all commands, but only one line per command */ - if( zPattern==0 ) zPattern = ""; - for(i=0; ip); - sqlite3_free(pSession->zName); - for(i=0; inFilter; i++){ - sqlite3_free(pSession->azFilter[i]); - } - sqlite3_free(pSession->azFilter); - memset(pSession, 0, sizeof(OpenSession)); -} -#endif - -/* -** Close all OpenSession objects and release all associated resources. -*/ -#if defined(SQLITE_ENABLE_SESSION) -static void session_close_all(ShellState *p){ - int i; - for(i=0; inSession; i++){ - session_close(&p->aSession[i]); - } - p->nSession = 0; -} -#else -# define session_close_all(X) -#endif - -/* -** Implementation of the xFilter function for an open session. Omit -** any tables named by ".session filter" but let all other table through. -*/ -#if defined(SQLITE_ENABLE_SESSION) -static int session_filter(void *pCtx, const char *zTab){ - OpenSession *pSession = (OpenSession*)pCtx; - int i; - for(i=0; inFilter; i++){ - if( sqlite3_strglob(pSession->azFilter[i], zTab)==0 ) return 0; - } - return 1; -} -#endif - -/* -** Try to deduce the type of file for zName based on its content. Return -** one of the SHELL_OPEN_* constants. -** -** If the file does not exist or is empty but its name looks like a ZIP -** archive and the dfltZip flag is true, then assume it is a ZIP archive. -** Otherwise, assume an ordinary database regardless of the filename if -** the type cannot be determined from content. -*/ -int deduceDatabaseType(const char *zName, int dfltZip){ - FILE *f = fopen(zName, "rb"); - size_t n; - int rc = SHELL_OPEN_UNSPEC; - char zBuf[100]; - if( f==0 ){ - if( dfltZip && sqlite3_strlike("%.zip",zName,0)==0 ){ - return SHELL_OPEN_ZIPFILE; - }else{ - return SHELL_OPEN_NORMAL; - } - } - n = fread(zBuf, 16, 1, f); - if( n==1 && memcmp(zBuf, "SQLite format 3", 16)==0 ){ - fclose(f); - return SHELL_OPEN_NORMAL; - } - fseek(f, -25, SEEK_END); - n = fread(zBuf, 25, 1, f); - if( n==1 && memcmp(zBuf, "Start-Of-SQLite3-", 17)==0 ){ - rc = SHELL_OPEN_APPENDVFS; - }else{ - fseek(f, -22, SEEK_END); - n = fread(zBuf, 22, 1, f); - if( n==1 && zBuf[0]==0x50 && zBuf[1]==0x4b && zBuf[2]==0x05 - && zBuf[3]==0x06 ){ - rc = SHELL_OPEN_ZIPFILE; - }else if( n==0 && dfltZip && sqlite3_strlike("%.zip",zName,0)==0 ){ - rc = SHELL_OPEN_ZIPFILE; - } - } - fclose(f); - return rc; -} - -#ifdef SQLITE_ENABLE_DESERIALIZE -/* -** Reconstruct an in-memory database using the output from the "dbtotxt" -** program. Read content from the file in p->zDbFilename. If p->zDbFilename -** is 0, then read from standard input. -*/ -static unsigned char *readHexDb(ShellState *p, int *pnData){ - unsigned char *a = 0; - int nLine; - int n = 0; - int pgsz = 0; - int iOffset = 0; - int j, k; - int rc; - FILE *in; - unsigned int x[16]; - char zLine[1000]; - if( p->zDbFilename ){ - in = fopen(p->zDbFilename, "r"); - if( in==0 ){ - utf8_printf(stderr, "cannot open \"%s\" for reading\n", p->zDbFilename); - return 0; - } - nLine = 0; - }else{ - in = p->in; - nLine = p->lineno; - if( in==0 ) in = stdin; - } - *pnData = 0; - nLine++; - if( fgets(zLine, sizeof(zLine), in)==0 ) goto readHexDb_error; - rc = sscanf(zLine, "| size %d pagesize %d", &n, &pgsz); - if( rc!=2 ) goto readHexDb_error; - if( n<0 ) goto readHexDb_error; - if( pgsz<512 || pgsz>65536 || (pgsz&(pgsz-1))!=0 ) goto readHexDb_error; - n = (n+pgsz-1)&~(pgsz-1); /* Round n up to the next multiple of pgsz */ - a = sqlite3_malloc( n ? n : 1 ); - if( a==0 ){ - utf8_printf(stderr, "Out of memory!\n"); - goto readHexDb_error; - } - memset(a, 0, n); - if( pgsz<512 || pgsz>65536 || (pgsz & (pgsz-1))!=0 ){ - utf8_printf(stderr, "invalid pagesize\n"); - goto readHexDb_error; - } - for(nLine++; fgets(zLine, sizeof(zLine), in)!=0; nLine++){ - rc = sscanf(zLine, "| page %d offset %d", &j, &k); - if( rc==2 ){ - iOffset = k; - continue; - } - if( strncmp(zLine, "| end ", 6)==0 ){ - break; - } - rc = sscanf(zLine,"| %d: %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x", - &j, &x[0], &x[1], &x[2], &x[3], &x[4], &x[5], &x[6], &x[7], - &x[8], &x[9], &x[10], &x[11], &x[12], &x[13], &x[14], &x[15]); - if( rc==17 ){ - k = iOffset+j; - if( k+16<=n ){ - int ii; - for(ii=0; ii<16; ii++) a[k+ii] = x[ii]&0xff; - } - } - } - *pnData = n; - if( in!=p->in ){ - fclose(in); - }else{ - p->lineno = nLine; - } - return a; - -readHexDb_error: - if( in!=p->in ){ - fclose(in); - }else{ - while( fgets(zLine, sizeof(zLine), p->in)!=0 ){ - nLine++; - if(strncmp(zLine, "| end ", 6)==0 ) break; - } - p->lineno = nLine; - } - sqlite3_free(a); - utf8_printf(stderr,"Error on line %d of --hexdb input\n", nLine); - return 0; -} -#endif /* SQLITE_ENABLE_DESERIALIZE */ - -/* -** Scalar function "shell_int32". The first argument to this function -** must be a blob. The second a non-negative integer. This function -** reads and returns a 32-bit big-endian integer from byte -** offset (4*) of the blob. -*/ -static void shellInt32( - sqlite3_context *context, - int argc, - sqlite3_value **argv -){ - const unsigned char *pBlob; - int nBlob; - int iInt; - - UNUSED_PARAMETER(argc); - nBlob = sqlite3_value_bytes(argv[0]); - pBlob = (const unsigned char*)sqlite3_value_blob(argv[0]); - iInt = sqlite3_value_int(argv[1]); - - if( iInt>=0 && (iInt+1)*4<=nBlob ){ - const unsigned char *a = &pBlob[iInt*4]; - sqlite3_int64 iVal = ((sqlite3_int64)a[0]<<24) - + ((sqlite3_int64)a[1]<<16) - + ((sqlite3_int64)a[2]<< 8) - + ((sqlite3_int64)a[3]<< 0); - sqlite3_result_int64(context, iVal); - } -} - -/* -** Scalar function "shell_idquote(X)" returns string X quoted as an identifier, -** using "..." with internal double-quote characters doubled. -*/ -static void shellIdQuote( - sqlite3_context *context, - int argc, - sqlite3_value **argv -){ - const char *zName = (const char*)sqlite3_value_text(argv[0]); - UNUSED_PARAMETER(argc); - if( zName ){ - char *z = sqlite3_mprintf("\"%w\"", zName); - sqlite3_result_text(context, z, -1, sqlite3_free); - } -} - -/* -** Scalar function "shell_escape_crnl" used by the .recover command. -** The argument passed to this function is the output of built-in -** function quote(). If the first character of the input is "'", -** indicating that the value passed to quote() was a text value, -** then this function searches the input for "\n" and "\r" characters -** and adds a wrapper similar to the following: -** -** replace(replace(, '\n', char(10), '\r', char(13)); -** -** Or, if the first character of the input is not "'", then a copy -** of the input is returned. -*/ -static void shellEscapeCrnl( - sqlite3_context *context, - int argc, - sqlite3_value **argv -){ - const char *zText = (const char*)sqlite3_value_text(argv[0]); - UNUSED_PARAMETER(argc); - if( zText[0]=='\'' ){ - int nText = sqlite3_value_bytes(argv[0]); - int i; - char zBuf1[20]; - char zBuf2[20]; - const char *zNL = 0; - const char *zCR = 0; - int nCR = 0; - int nNL = 0; - - for(i=0; zText[i]; i++){ - if( zNL==0 && zText[i]=='\n' ){ - zNL = unused_string(zText, "\\n", "\\012", zBuf1); - nNL = (int)strlen(zNL); - } - if( zCR==0 && zText[i]=='\r' ){ - zCR = unused_string(zText, "\\r", "\\015", zBuf2); - nCR = (int)strlen(zCR); - } - } - - if( zNL || zCR ){ - int iOut = 0; - i64 nMax = (nNL > nCR) ? nNL : nCR; - i64 nAlloc = nMax * nText + (nMax+64)*2; - char *zOut = (char*)sqlite3_malloc64(nAlloc); - if( zOut==0 ){ - sqlite3_result_error_nomem(context); - return; - } - - if( zNL && zCR ){ - memcpy(&zOut[iOut], "replace(replace(", 16); - iOut += 16; - }else{ - memcpy(&zOut[iOut], "replace(", 8); - iOut += 8; - } - for(i=0; zText[i]; i++){ - if( zText[i]=='\n' ){ - memcpy(&zOut[iOut], zNL, nNL); - iOut += nNL; - }else if( zText[i]=='\r' ){ - memcpy(&zOut[iOut], zCR, nCR); - iOut += nCR; - }else{ - zOut[iOut] = zText[i]; - iOut++; - } - } - - if( zNL ){ - memcpy(&zOut[iOut], ",'", 2); iOut += 2; - memcpy(&zOut[iOut], zNL, nNL); iOut += nNL; - memcpy(&zOut[iOut], "', char(10))", 12); iOut += 12; - } - if( zCR ){ - memcpy(&zOut[iOut], ",'", 2); iOut += 2; - memcpy(&zOut[iOut], zCR, nCR); iOut += nCR; - memcpy(&zOut[iOut], "', char(13))", 12); iOut += 12; - } - - sqlite3_result_text(context, zOut, iOut, SQLITE_TRANSIENT); - sqlite3_free(zOut); - return; - } - } - - sqlite3_result_value(context, argv[0]); -} - -/* Flags for open_db(). -** -** The default behavior of open_db() is to exit(1) if the database fails to -** open. The OPEN_DB_KEEPALIVE flag changes that so that it prints an error -** but still returns without calling exit. -** -** The OPEN_DB_ZIPFILE flag causes open_db() to prefer to open files as a -** ZIP archive if the file does not exist or is empty and its name matches -** the *.zip pattern. -*/ -#define OPEN_DB_KEEPALIVE 0x001 /* Return after error if true */ -#define OPEN_DB_ZIPFILE 0x002 /* Open as ZIP if name matches *.zip */ - -/* -** Make sure the database is open. If it is not, then open it. If -** the database fails to open, print an error message and exit. -*/ -static void open_db(ShellState *p, int openFlags){ - if( p->db==0 ){ - if( p->openMode==SHELL_OPEN_UNSPEC ){ - if( p->zDbFilename==0 || p->zDbFilename[0]==0 ){ - p->openMode = SHELL_OPEN_NORMAL; - }else{ - p->openMode = (u8)deduceDatabaseType(p->zDbFilename, - (openFlags & OPEN_DB_ZIPFILE)!=0); - } - } - switch( p->openMode ){ - case SHELL_OPEN_APPENDVFS: { - sqlite3_open_v2(p->zDbFilename, &p->db, - SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE|p->openFlags, "apndvfs"); - break; - } - case SHELL_OPEN_HEXDB: - case SHELL_OPEN_DESERIALIZE: { - sqlite3_open(0, &p->db); - break; - } - case SHELL_OPEN_ZIPFILE: { - sqlite3_open(":memory:", &p->db); - break; - } - case SHELL_OPEN_READONLY: { - sqlite3_open_v2(p->zDbFilename, &p->db, - SQLITE_OPEN_READONLY|p->openFlags, 0); - break; - } - case SHELL_OPEN_UNSPEC: - case SHELL_OPEN_NORMAL: { - sqlite3_open_v2(p->zDbFilename, &p->db, - SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE|p->openFlags, 0); - break; - } - } - globalDb = p->db; - if( p->db==0 || SQLITE_OK!=sqlite3_errcode(p->db) ){ - utf8_printf(stderr,"Error: unable to open database \"%s\": %s\n", - p->zDbFilename, sqlite3_errmsg(p->db)); - if( openFlags & OPEN_DB_KEEPALIVE ){ - sqlite3_open(":memory:", &p->db); - return; - } - exit(1); - } -#ifndef SQLITE_OMIT_LOAD_EXTENSION - sqlite3_enable_load_extension(p->db, 1); -#endif - sqlite3_fileio_init(p->db, 0, 0); - sqlite3_shathree_init(p->db, 0, 0); - sqlite3_completion_init(p->db, 0, 0); - sqlite3_uint_init(p->db, 0, 0); - sqlite3_decimal_init(p->db, 0, 0); - sqlite3_ieee_init(p->db, 0, 0); -#if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_ENABLE_DBPAGE_VTAB) - sqlite3_dbdata_init(p->db, 0, 0); -#endif -#ifdef SQLITE_HAVE_ZLIB - sqlite3_zipfile_init(p->db, 0, 0); - sqlite3_sqlar_init(p->db, 0, 0); -#endif - sqlite3_create_function(p->db, "shell_add_schema", 3, SQLITE_UTF8, 0, - shellAddSchemaName, 0, 0); - sqlite3_create_function(p->db, "shell_module_schema", 1, SQLITE_UTF8, 0, - shellModuleSchema, 0, 0); - sqlite3_create_function(p->db, "shell_putsnl", 1, SQLITE_UTF8, p, - shellPutsFunc, 0, 0); - sqlite3_create_function(p->db, "shell_escape_crnl", 1, SQLITE_UTF8, 0, - shellEscapeCrnl, 0, 0); - sqlite3_create_function(p->db, "shell_int32", 2, SQLITE_UTF8, 0, - shellInt32, 0, 0); - sqlite3_create_function(p->db, "shell_idquote", 1, SQLITE_UTF8, 0, - shellIdQuote, 0, 0); -#ifndef SQLITE_NOHAVE_SYSTEM - sqlite3_create_function(p->db, "edit", 1, SQLITE_UTF8, 0, - editFunc, 0, 0); - sqlite3_create_function(p->db, "edit", 2, SQLITE_UTF8, 0, - editFunc, 0, 0); -#endif - if( p->openMode==SHELL_OPEN_ZIPFILE ){ - char *zSql = sqlite3_mprintf( - "CREATE VIRTUAL TABLE zip USING zipfile(%Q);", p->zDbFilename); - sqlite3_exec(p->db, zSql, 0, 0, 0); - sqlite3_free(zSql); - } -#ifdef SQLITE_ENABLE_DESERIALIZE - else - if( p->openMode==SHELL_OPEN_DESERIALIZE || p->openMode==SHELL_OPEN_HEXDB ){ - int rc; - int nData = 0; - unsigned char *aData; - if( p->openMode==SHELL_OPEN_DESERIALIZE ){ - aData = (unsigned char*)readFile(p->zDbFilename, &nData); - }else{ - aData = readHexDb(p, &nData); - if( aData==0 ){ - return; - } - } - rc = sqlite3_deserialize(p->db, "main", aData, nData, nData, - SQLITE_DESERIALIZE_RESIZEABLE | - SQLITE_DESERIALIZE_FREEONCLOSE); - if( rc ){ - utf8_printf(stderr, "Error: sqlite3_deserialize() returns %d\n", rc); - } - if( p->szMax>0 ){ - sqlite3_file_control(p->db, "main", SQLITE_FCNTL_SIZE_LIMIT, &p->szMax); - } - } -#endif - } -} - -/* -** Attempt to close the databaes connection. Report errors. -*/ -void close_db(sqlite3 *db){ - int rc = sqlite3_close(db); - if( rc ){ - utf8_printf(stderr, "Error: sqlite3_close() returns %d: %s\n", - rc, sqlite3_errmsg(db)); - } -} - -#if HAVE_READLINE || HAVE_EDITLINE -/* -** Readline completion callbacks -*/ -static char *readline_completion_generator(const char *text, int state){ - static sqlite3_stmt *pStmt = 0; - char *zRet; - if( state==0 ){ - char *zSql; - sqlite3_finalize(pStmt); - zSql = sqlite3_mprintf("SELECT DISTINCT candidate COLLATE nocase" - " FROM completion(%Q) ORDER BY 1", text); - sqlite3_prepare_v2(globalDb, zSql, -1, &pStmt, 0); - sqlite3_free(zSql); - } - if( sqlite3_step(pStmt)==SQLITE_ROW ){ - zRet = strdup((const char*)sqlite3_column_text(pStmt, 0)); - }else{ - sqlite3_finalize(pStmt); - pStmt = 0; - zRet = 0; - } - return zRet; -} -static char **readline_completion(const char *zText, int iStart, int iEnd){ - rl_attempted_completion_over = 1; - return rl_completion_matches(zText, readline_completion_generator); -} - -#elif HAVE_LINENOISE -/* -** Linenoise completion callback -*/ -static void linenoise_completion(const char *zLine, linenoiseCompletions *lc){ - int nLine = strlen30(zLine); - int i, iStart; - sqlite3_stmt *pStmt = 0; - char *zSql; - char zBuf[1000]; - - if( nLine>sizeof(zBuf)-30 ) return; - if( zLine[0]=='.' || zLine[0]=='#') return; - for(i=nLine-1; i>=0 && (isalnum(zLine[i]) || zLine[i]=='_'); i--){} - if( i==nLine-1 ) return; - iStart = i+1; - memcpy(zBuf, zLine, iStart); - zSql = sqlite3_mprintf("SELECT DISTINCT candidate COLLATE nocase" - " FROM completion(%Q,%Q) ORDER BY 1", - &zLine[iStart], zLine); - sqlite3_prepare_v2(globalDb, zSql, -1, &pStmt, 0); - sqlite3_free(zSql); - sqlite3_exec(globalDb, "PRAGMA page_count", 0, 0, 0); /* Load the schema */ - while( sqlite3_step(pStmt)==SQLITE_ROW ){ - const char *zCompletion = (const char*)sqlite3_column_text(pStmt, 0); - int nCompletion = sqlite3_column_bytes(pStmt, 0); - if( iStart+nCompletion < sizeof(zBuf)-1 ){ - memcpy(zBuf+iStart, zCompletion, nCompletion+1); - linenoiseAddCompletion(lc, zBuf); - } - } - sqlite3_finalize(pStmt); -} -#endif - -/* -** Do C-language style dequoting. -** -** \a -> alarm -** \b -> backspace -** \t -> tab -** \n -> newline -** \v -> vertical tab -** \f -> form feed -** \r -> carriage return -** \s -> space -** \" -> " -** \' -> ' -** \\ -> backslash -** \NNN -> ascii character NNN in octal -*/ -static void resolve_backslashes(char *z){ - int i, j; - char c; - while( *z && *z!='\\' ) z++; - for(i=j=0; (c = z[i])!=0; i++, j++){ - if( c=='\\' && z[i+1]!=0 ){ - c = z[++i]; - if( c=='a' ){ - c = '\a'; - }else if( c=='b' ){ - c = '\b'; - }else if( c=='t' ){ - c = '\t'; - }else if( c=='n' ){ - c = '\n'; - }else if( c=='v' ){ - c = '\v'; - }else if( c=='f' ){ - c = '\f'; - }else if( c=='r' ){ - c = '\r'; - }else if( c=='"' ){ - c = '"'; - }else if( c=='\'' ){ - c = '\''; - }else if( c=='\\' ){ - c = '\\'; - }else if( c>='0' && c<='7' ){ - c -= '0'; - if( z[i+1]>='0' && z[i+1]<='7' ){ - i++; - c = (c<<3) + z[i] - '0'; - if( z[i+1]>='0' && z[i+1]<='7' ){ - i++; - c = (c<<3) + z[i] - '0'; - } - } - } - } - z[j] = c; - } - if( j=0; i++){} - }else{ - for(i=0; zArg[i]>='0' && zArg[i]<='9'; i++){} - } - if( i>0 && zArg[i]==0 ) return (int)(integerValue(zArg) & 0xffffffff); - if( sqlite3_stricmp(zArg, "on")==0 || sqlite3_stricmp(zArg,"yes")==0 ){ - return 1; - } - if( sqlite3_stricmp(zArg, "off")==0 || sqlite3_stricmp(zArg,"no")==0 ){ - return 0; - } - utf8_printf(stderr, "ERROR: Not a boolean value: \"%s\". Assuming \"no\".\n", - zArg); - return 0; -} - -/* -** Set or clear a shell flag according to a boolean value. -*/ -static void setOrClearFlag(ShellState *p, unsigned mFlag, const char *zArg){ - if( booleanValue(zArg) ){ - ShellSetFlag(p, mFlag); - }else{ - ShellClearFlag(p, mFlag); - } -} - -/* -** Close an output file, assuming it is not stderr or stdout -*/ -static void output_file_close(FILE *f){ - if( f && f!=stdout && f!=stderr ) fclose(f); -} - -/* -** Try to open an output file. The names "stdout" and "stderr" are -** recognized and do the right thing. NULL is returned if the output -** filename is "off". -*/ -static FILE *output_file_open(const char *zFile, int bTextMode){ - FILE *f; - if( strcmp(zFile,"stdout")==0 ){ - f = stdout; - }else if( strcmp(zFile, "stderr")==0 ){ - f = stderr; - }else if( strcmp(zFile, "off")==0 ){ - f = 0; - }else{ - f = fopen(zFile, bTextMode ? "w" : "wb"); - if( f==0 ){ - utf8_printf(stderr, "Error: cannot open \"%s\"\n", zFile); - } - } - return f; -} - -#ifndef SQLITE_OMIT_TRACE -/* -** A routine for handling output from sqlite3_trace(). -*/ -static int sql_trace_callback( - unsigned mType, /* The trace type */ - void *pArg, /* The ShellState pointer */ - void *pP, /* Usually a pointer to sqlite_stmt */ - void *pX /* Auxiliary output */ -){ - ShellState *p = (ShellState*)pArg; - sqlite3_stmt *pStmt; - const char *zSql; - int nSql; - if( p->traceOut==0 ) return 0; - if( mType==SQLITE_TRACE_CLOSE ){ - utf8_printf(p->traceOut, "-- closing database connection\n"); - return 0; - } - if( mType!=SQLITE_TRACE_ROW && ((const char*)pX)[0]=='-' ){ - zSql = (const char*)pX; - }else{ - pStmt = (sqlite3_stmt*)pP; - switch( p->eTraceType ){ - case SHELL_TRACE_EXPANDED: { - zSql = sqlite3_expanded_sql(pStmt); - break; - } -#ifdef SQLITE_ENABLE_NORMALIZE - case SHELL_TRACE_NORMALIZED: { - zSql = sqlite3_normalized_sql(pStmt); - break; - } -#endif - default: { - zSql = sqlite3_sql(pStmt); - break; - } - } - } - if( zSql==0 ) return 0; - nSql = strlen30(zSql); - while( nSql>0 && zSql[nSql-1]==';' ){ nSql--; } - switch( mType ){ - case SQLITE_TRACE_ROW: - case SQLITE_TRACE_STMT: { - utf8_printf(p->traceOut, "%.*s;\n", nSql, zSql); - break; - } - case SQLITE_TRACE_PROFILE: { - sqlite3_int64 nNanosec = *(sqlite3_int64*)pX; - utf8_printf(p->traceOut, "%.*s; -- %lld ns\n", nSql, zSql, nNanosec); - break; - } - } - return 0; -} -#endif - -/* -** A no-op routine that runs with the ".breakpoint" doc-command. This is -** a useful spot to set a debugger breakpoint. -*/ -static void test_breakpoint(void){ - static int nCall = 0; - nCall++; -} - -/* -** An object used to read a CSV and other files for import. -*/ -typedef struct ImportCtx ImportCtx; -struct ImportCtx { - const char *zFile; /* Name of the input file */ - FILE *in; /* Read the CSV text from this input stream */ - int (SQLITE_CDECL *xCloser)(FILE*); /* Func to close in */ - char *z; /* Accumulated text for a field */ - int n; /* Number of bytes in z */ - int nAlloc; /* Space allocated for z[] */ - int nLine; /* Current line number */ - int nRow; /* Number of rows imported */ - int nErr; /* Number of errors encountered */ - int bNotFirst; /* True if one or more bytes already read */ - int cTerm; /* Character that terminated the most recent field */ - int cColSep; /* The column separator character. (Usually ",") */ - int cRowSep; /* The row separator character. (Usually "\n") */ -}; - -/* Clean up resourced used by an ImportCtx */ -static void import_cleanup(ImportCtx *p){ - if( p->in!=0 && p->xCloser!=0 ){ - p->xCloser(p->in); - p->in = 0; - } - sqlite3_free(p->z); - p->z = 0; -} - -/* Append a single byte to z[] */ -static void import_append_char(ImportCtx *p, int c){ - if( p->n+1>=p->nAlloc ){ - p->nAlloc += p->nAlloc + 100; - p->z = sqlite3_realloc64(p->z, p->nAlloc); - if( p->z==0 ) shell_out_of_memory(); - } - p->z[p->n++] = (char)c; -} - -/* Read a single field of CSV text. Compatible with rfc4180 and extended -** with the option of having a separator other than ",". -** -** + Input comes from p->in. -** + Store results in p->z of length p->n. Space to hold p->z comes -** from sqlite3_malloc64(). -** + Use p->cSep as the column separator. The default is ",". -** + Use p->rSep as the row separator. The default is "\n". -** + Keep track of the line number in p->nLine. -** + Store the character that terminates the field in p->cTerm. Store -** EOF on end-of-file. -** + Report syntax errors on stderr -*/ -static char *SQLITE_CDECL csv_read_one_field(ImportCtx *p){ - int c; - int cSep = p->cColSep; - int rSep = p->cRowSep; - p->n = 0; - c = fgetc(p->in); - if( c==EOF || seenInterrupt ){ - p->cTerm = EOF; - return 0; - } - if( c=='"' ){ - int pc, ppc; - int startLine = p->nLine; - int cQuote = c; - pc = ppc = 0; - while( 1 ){ - c = fgetc(p->in); - if( c==rSep ) p->nLine++; - if( c==cQuote ){ - if( pc==cQuote ){ - pc = 0; - continue; - } - } - if( (c==cSep && pc==cQuote) - || (c==rSep && pc==cQuote) - || (c==rSep && pc=='\r' && ppc==cQuote) - || (c==EOF && pc==cQuote) - ){ - do{ p->n--; }while( p->z[p->n]!=cQuote ); - p->cTerm = c; - break; - } - if( pc==cQuote && c!='\r' ){ - utf8_printf(stderr, "%s:%d: unescaped %c character\n", - p->zFile, p->nLine, cQuote); - } - if( c==EOF ){ - utf8_printf(stderr, "%s:%d: unterminated %c-quoted field\n", - p->zFile, startLine, cQuote); - p->cTerm = c; - break; - } - import_append_char(p, c); - ppc = pc; - pc = c; - } - }else{ - /* If this is the first field being parsed and it begins with the - ** UTF-8 BOM (0xEF BB BF) then skip the BOM */ - if( (c&0xff)==0xef && p->bNotFirst==0 ){ - import_append_char(p, c); - c = fgetc(p->in); - if( (c&0xff)==0xbb ){ - import_append_char(p, c); - c = fgetc(p->in); - if( (c&0xff)==0xbf ){ - p->bNotFirst = 1; - p->n = 0; - return csv_read_one_field(p); - } - } - } - while( c!=EOF && c!=cSep && c!=rSep ){ - import_append_char(p, c); - c = fgetc(p->in); - } - if( c==rSep ){ - p->nLine++; - if( p->n>0 && p->z[p->n-1]=='\r' ) p->n--; - } - p->cTerm = c; - } - if( p->z ) p->z[p->n] = 0; - p->bNotFirst = 1; - return p->z; -} - -/* Read a single field of ASCII delimited text. -** -** + Input comes from p->in. -** + Store results in p->z of length p->n. Space to hold p->z comes -** from sqlite3_malloc64(). -** + Use p->cSep as the column separator. The default is "\x1F". -** + Use p->rSep as the row separator. The default is "\x1E". -** + Keep track of the row number in p->nLine. -** + Store the character that terminates the field in p->cTerm. Store -** EOF on end-of-file. -** + Report syntax errors on stderr -*/ -static char *SQLITE_CDECL ascii_read_one_field(ImportCtx *p){ - int c; - int cSep = p->cColSep; - int rSep = p->cRowSep; - p->n = 0; - c = fgetc(p->in); - if( c==EOF || seenInterrupt ){ - p->cTerm = EOF; - return 0; - } - while( c!=EOF && c!=cSep && c!=rSep ){ - import_append_char(p, c); - c = fgetc(p->in); - } - if( c==rSep ){ - p->nLine++; - } - p->cTerm = c; - if( p->z ) p->z[p->n] = 0; - return p->z; -} - -/* -** Try to transfer data for table zTable. If an error is seen while -** moving forward, try to go backwards. The backwards movement won't -** work for WITHOUT ROWID tables. -*/ -static void tryToCloneData( - ShellState *p, - sqlite3 *newDb, - const char *zTable -){ - sqlite3_stmt *pQuery = 0; - sqlite3_stmt *pInsert = 0; - char *zQuery = 0; - char *zInsert = 0; - int rc; - int i, j, n; - int nTable = strlen30(zTable); - int k = 0; - int cnt = 0; - const int spinRate = 10000; - - zQuery = sqlite3_mprintf("SELECT * FROM \"%w\"", zTable); - rc = sqlite3_prepare_v2(p->db, zQuery, -1, &pQuery, 0); - if( rc ){ - utf8_printf(stderr, "Error %d: %s on [%s]\n", - sqlite3_extended_errcode(p->db), sqlite3_errmsg(p->db), - zQuery); - goto end_data_xfer; - } - n = sqlite3_column_count(pQuery); - zInsert = sqlite3_malloc64(200 + nTable + n*3); - if( zInsert==0 ) shell_out_of_memory(); - sqlite3_snprintf(200+nTable,zInsert, - "INSERT OR IGNORE INTO \"%s\" VALUES(?", zTable); - i = strlen30(zInsert); - for(j=1; jdb, zQuery, -1, &pQuery, 0); - if( rc ){ - utf8_printf(stderr, "Warning: cannot step \"%s\" backwards", zTable); - break; - } - } /* End for(k=0...) */ - -end_data_xfer: - sqlite3_finalize(pQuery); - sqlite3_finalize(pInsert); - sqlite3_free(zQuery); - sqlite3_free(zInsert); -} - - -/* -** Try to transfer all rows of the schema that match zWhere. For -** each row, invoke xForEach() on the object defined by that row. -** If an error is encountered while moving forward through the -** sqlite_schema table, try again moving backwards. -*/ -static void tryToCloneSchema( - ShellState *p, - sqlite3 *newDb, - const char *zWhere, - void (*xForEach)(ShellState*,sqlite3*,const char*) -){ - sqlite3_stmt *pQuery = 0; - char *zQuery = 0; - int rc; - const unsigned char *zName; - const unsigned char *zSql; - char *zErrMsg = 0; - - zQuery = sqlite3_mprintf("SELECT name, sql FROM sqlite_schema" - " WHERE %s", zWhere); - rc = sqlite3_prepare_v2(p->db, zQuery, -1, &pQuery, 0); - if( rc ){ - utf8_printf(stderr, "Error: (%d) %s on [%s]\n", - sqlite3_extended_errcode(p->db), sqlite3_errmsg(p->db), - zQuery); - goto end_schema_xfer; - } - while( (rc = sqlite3_step(pQuery))==SQLITE_ROW ){ - zName = sqlite3_column_text(pQuery, 0); - zSql = sqlite3_column_text(pQuery, 1); - printf("%s... ", zName); fflush(stdout); - sqlite3_exec(newDb, (const char*)zSql, 0, 0, &zErrMsg); - if( zErrMsg ){ - utf8_printf(stderr, "Error: %s\nSQL: [%s]\n", zErrMsg, zSql); - sqlite3_free(zErrMsg); - zErrMsg = 0; - } - if( xForEach ){ - xForEach(p, newDb, (const char*)zName); - } - printf("done\n"); - } - if( rc!=SQLITE_DONE ){ - sqlite3_finalize(pQuery); - sqlite3_free(zQuery); - zQuery = sqlite3_mprintf("SELECT name, sql FROM sqlite_schema" - " WHERE %s ORDER BY rowid DESC", zWhere); - rc = sqlite3_prepare_v2(p->db, zQuery, -1, &pQuery, 0); - if( rc ){ - utf8_printf(stderr, "Error: (%d) %s on [%s]\n", - sqlite3_extended_errcode(p->db), sqlite3_errmsg(p->db), - zQuery); - goto end_schema_xfer; - } - while( (rc = sqlite3_step(pQuery))==SQLITE_ROW ){ - zName = sqlite3_column_text(pQuery, 0); - zSql = sqlite3_column_text(pQuery, 1); - printf("%s... ", zName); fflush(stdout); - sqlite3_exec(newDb, (const char*)zSql, 0, 0, &zErrMsg); - if( zErrMsg ){ - utf8_printf(stderr, "Error: %s\nSQL: [%s]\n", zErrMsg, zSql); - sqlite3_free(zErrMsg); - zErrMsg = 0; - } - if( xForEach ){ - xForEach(p, newDb, (const char*)zName); - } - printf("done\n"); - } - } -end_schema_xfer: - sqlite3_finalize(pQuery); - sqlite3_free(zQuery); -} - -/* -** Open a new database file named "zNewDb". Try to recover as much information -** as possible out of the main database (which might be corrupt) and write it -** into zNewDb. -*/ -static void tryToClone(ShellState *p, const char *zNewDb){ - int rc; - sqlite3 *newDb = 0; - if( access(zNewDb,0)==0 ){ - utf8_printf(stderr, "File \"%s\" already exists.\n", zNewDb); - return; - } - rc = sqlite3_open(zNewDb, &newDb); - if( rc ){ - utf8_printf(stderr, "Cannot create output database: %s\n", - sqlite3_errmsg(newDb)); - }else{ - sqlite3_exec(p->db, "PRAGMA writable_schema=ON;", 0, 0, 0); - sqlite3_exec(newDb, "BEGIN EXCLUSIVE;", 0, 0, 0); - tryToCloneSchema(p, newDb, "type='table'", tryToCloneData); - tryToCloneSchema(p, newDb, "type!='table'", 0); - sqlite3_exec(newDb, "COMMIT;", 0, 0, 0); - sqlite3_exec(p->db, "PRAGMA writable_schema=OFF;", 0, 0, 0); - } - close_db(newDb); -} - -/* -** Change the output file back to stdout. -** -** If the p->doXdgOpen flag is set, that means the output was being -** redirected to a temporary file named by p->zTempFile. In that case, -** launch start/open/xdg-open on that temporary file. -*/ -static void output_reset(ShellState *p){ - if( p->outfile[0]=='|' ){ -#ifndef SQLITE_OMIT_POPEN - pclose(p->out); -#endif - }else{ - output_file_close(p->out); -#ifndef SQLITE_NOHAVE_SYSTEM - if( p->doXdgOpen ){ - const char *zXdgOpenCmd = -#if defined(_WIN32) - "start"; -#elif defined(__APPLE__) - "open"; -#else - "xdg-open"; -#endif - char *zCmd; - zCmd = sqlite3_mprintf("%s %s", zXdgOpenCmd, p->zTempFile); - if( system(zCmd) ){ - utf8_printf(stderr, "Failed: [%s]\n", zCmd); - }else{ - /* Give the start/open/xdg-open command some time to get - ** going before we continue, and potential delete the - ** p->zTempFile data file out from under it */ - sqlite3_sleep(2000); - } - sqlite3_free(zCmd); - outputModePop(p); - p->doXdgOpen = 0; - } -#endif /* !defined(SQLITE_NOHAVE_SYSTEM) */ - } - p->outfile[0] = 0; - p->out = stdout; -} - -/* -** Run an SQL command and return the single integer result. -*/ -static int db_int(ShellState *p, const char *zSql){ - sqlite3_stmt *pStmt; - int res = 0; - sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0); - if( pStmt && sqlite3_step(pStmt)==SQLITE_ROW ){ - res = sqlite3_column_int(pStmt,0); - } - sqlite3_finalize(pStmt); - return res; -} - -/* -** Convert a 2-byte or 4-byte big-endian integer into a native integer -*/ -static unsigned int get2byteInt(unsigned char *a){ - return (a[0]<<8) + a[1]; -} -static unsigned int get4byteInt(unsigned char *a){ - return (a[0]<<24) + (a[1]<<16) + (a[2]<<8) + a[3]; -} - -/* -** Implementation of the ".dbinfo" command. -** -** Return 1 on error, 2 to exit, and 0 otherwise. -*/ -static int shell_dbinfo_command(ShellState *p, int nArg, char **azArg){ - static const struct { const char *zName; int ofst; } aField[] = { - { "file change counter:", 24 }, - { "database page count:", 28 }, - { "freelist page count:", 36 }, - { "schema cookie:", 40 }, - { "schema format:", 44 }, - { "default cache size:", 48 }, - { "autovacuum top root:", 52 }, - { "incremental vacuum:", 64 }, - { "text encoding:", 56 }, - { "user version:", 60 }, - { "application id:", 68 }, - { "software version:", 96 }, - }; - static const struct { const char *zName; const char *zSql; } aQuery[] = { - { "number of tables:", - "SELECT count(*) FROM %s WHERE type='table'" }, - { "number of indexes:", - "SELECT count(*) FROM %s WHERE type='index'" }, - { "number of triggers:", - "SELECT count(*) FROM %s WHERE type='trigger'" }, - { "number of views:", - "SELECT count(*) FROM %s WHERE type='view'" }, - { "schema size:", - "SELECT total(length(sql)) FROM %s" }, - }; - int i, rc; - unsigned iDataVersion; - char *zSchemaTab; - char *zDb = nArg>=2 ? azArg[1] : "main"; - sqlite3_stmt *pStmt = 0; - unsigned char aHdr[100]; - open_db(p, 0); - if( p->db==0 ) return 1; - rc = sqlite3_prepare_v2(p->db, - "SELECT data FROM sqlite_dbpage(?1) WHERE pgno=1", - -1, &pStmt, 0); - if( rc ){ - utf8_printf(stderr, "error: %s\n", sqlite3_errmsg(p->db)); - sqlite3_finalize(pStmt); - return 1; - } - sqlite3_bind_text(pStmt, 1, zDb, -1, SQLITE_STATIC); - if( sqlite3_step(pStmt)==SQLITE_ROW - && sqlite3_column_bytes(pStmt,0)>100 - ){ - memcpy(aHdr, sqlite3_column_blob(pStmt,0), 100); - sqlite3_finalize(pStmt); - }else{ - raw_printf(stderr, "unable to read database header\n"); - sqlite3_finalize(pStmt); - return 1; - } - i = get2byteInt(aHdr+16); - if( i==1 ) i = 65536; - utf8_printf(p->out, "%-20s %d\n", "database page size:", i); - utf8_printf(p->out, "%-20s %d\n", "write format:", aHdr[18]); - utf8_printf(p->out, "%-20s %d\n", "read format:", aHdr[19]); - utf8_printf(p->out, "%-20s %d\n", "reserved bytes:", aHdr[20]); - for(i=0; iout, "%-20s %u", aField[i].zName, val); - switch( ofst ){ - case 56: { - if( val==1 ) raw_printf(p->out, " (utf8)"); - if( val==2 ) raw_printf(p->out, " (utf16le)"); - if( val==3 ) raw_printf(p->out, " (utf16be)"); - } - } - raw_printf(p->out, "\n"); - } - if( zDb==0 ){ - zSchemaTab = sqlite3_mprintf("main.sqlite_schema"); - }else if( strcmp(zDb,"temp")==0 ){ - zSchemaTab = sqlite3_mprintf("%s", "sqlite_temp_schema"); - }else{ - zSchemaTab = sqlite3_mprintf("\"%w\".sqlite_schema", zDb); - } - for(i=0; iout, "%-20s %d\n", aQuery[i].zName, val); - } - sqlite3_free(zSchemaTab); - sqlite3_file_control(p->db, zDb, SQLITE_FCNTL_DATA_VERSION, &iDataVersion); - utf8_printf(p->out, "%-20s %u\n", "data version", iDataVersion); - return 0; -} - -/* -** Print the current sqlite3_errmsg() value to stderr and return 1. -*/ -static int shellDatabaseError(sqlite3 *db){ - const char *zErr = sqlite3_errmsg(db); - utf8_printf(stderr, "Error: %s\n", zErr); - return 1; -} - -/* -** Compare the pattern in zGlob[] against the text in z[]. Return TRUE -** if they match and FALSE (0) if they do not match. -** -** Globbing rules: -** -** '*' Matches any sequence of zero or more characters. -** -** '?' Matches exactly one character. -** -** [...] Matches one character from the enclosed list of -** characters. -** -** [^...] Matches one character not in the enclosed list. -** -** '#' Matches any sequence of one or more digits with an -** optional + or - sign in front -** -** ' ' Any span of whitespace matches any other span of -** whitespace. -** -** Extra whitespace at the end of z[] is ignored. -*/ -static int testcase_glob(const char *zGlob, const char *z){ - int c, c2; - int invert; - int seen; - - while( (c = (*(zGlob++)))!=0 ){ - if( IsSpace(c) ){ - if( !IsSpace(*z) ) return 0; - while( IsSpace(*zGlob) ) zGlob++; - while( IsSpace(*z) ) z++; - }else if( c=='*' ){ - while( (c=(*(zGlob++))) == '*' || c=='?' ){ - if( c=='?' && (*(z++))==0 ) return 0; - } - if( c==0 ){ - return 1; - }else if( c=='[' ){ - while( *z && testcase_glob(zGlob-1,z)==0 ){ - z++; - } - return (*z)!=0; - } - while( (c2 = (*(z++)))!=0 ){ - while( c2!=c ){ - c2 = *(z++); - if( c2==0 ) return 0; - } - if( testcase_glob(zGlob,z) ) return 1; - } - return 0; - }else if( c=='?' ){ - if( (*(z++))==0 ) return 0; - }else if( c=='[' ){ - int prior_c = 0; - seen = 0; - invert = 0; - c = *(z++); - if( c==0 ) return 0; - c2 = *(zGlob++); - if( c2=='^' ){ - invert = 1; - c2 = *(zGlob++); - } - if( c2==']' ){ - if( c==']' ) seen = 1; - c2 = *(zGlob++); - } - while( c2 && c2!=']' ){ - if( c2=='-' && zGlob[0]!=']' && zGlob[0]!=0 && prior_c>0 ){ - c2 = *(zGlob++); - if( c>=prior_c && c<=c2 ) seen = 1; - prior_c = 0; - }else{ - if( c==c2 ){ - seen = 1; - } - prior_c = c2; - } - c2 = *(zGlob++); - } - if( c2==0 || (seen ^ invert)==0 ) return 0; - }else if( c=='#' ){ - if( (z[0]=='-' || z[0]=='+') && IsDigit(z[1]) ) z++; - if( !IsDigit(z[0]) ) return 0; - z++; - while( IsDigit(z[0]) ){ z++; } - }else{ - if( c!=(*(z++)) ) return 0; - } - } - while( IsSpace(*z) ){ z++; } - return *z==0; -} - - -/* -** Compare the string as a command-line option with either one or two -** initial "-" characters. -*/ -static int optionMatch(const char *zStr, const char *zOpt){ - if( zStr[0]!='-' ) return 0; - zStr++; - if( zStr[0]=='-' ) zStr++; - return strcmp(zStr, zOpt)==0; -} - -/* -** Delete a file. -*/ -int shellDeleteFile(const char *zFilename){ - int rc; -#ifdef _WIN32 - wchar_t *z = sqlite3_win32_utf8_to_unicode(zFilename); - rc = _wunlink(z); - sqlite3_free(z); -#else - rc = unlink(zFilename); -#endif - return rc; -} - -/* -** Try to delete the temporary file (if there is one) and free the -** memory used to hold the name of the temp file. -*/ -static void clearTempFile(ShellState *p){ - if( p->zTempFile==0 ) return; - if( p->doXdgOpen ) return; - if( shellDeleteFile(p->zTempFile) ) return; - sqlite3_free(p->zTempFile); - p->zTempFile = 0; -} - -/* -** Create a new temp file name with the given suffix. -*/ -static void newTempFile(ShellState *p, const char *zSuffix){ - clearTempFile(p); - sqlite3_free(p->zTempFile); - p->zTempFile = 0; - if( p->db ){ - sqlite3_file_control(p->db, 0, SQLITE_FCNTL_TEMPFILENAME, &p->zTempFile); - } - if( p->zTempFile==0 ){ - /* If p->db is an in-memory database then the TEMPFILENAME file-control - ** will not work and we will need to fallback to guessing */ - char *zTemp; - sqlite3_uint64 r; - sqlite3_randomness(sizeof(r), &r); - zTemp = getenv("TEMP"); - if( zTemp==0 ) zTemp = getenv("TMP"); - if( zTemp==0 ){ -#ifdef _WIN32 - zTemp = "\\tmp"; -#else - zTemp = "/tmp"; -#endif - } - p->zTempFile = sqlite3_mprintf("%s/temp%llx.%s", zTemp, r, zSuffix); - }else{ - p->zTempFile = sqlite3_mprintf("%z.%s", p->zTempFile, zSuffix); - } - if( p->zTempFile==0 ){ - raw_printf(stderr, "out of memory\n"); - exit(1); - } -} - - -/* -** The implementation of SQL scalar function fkey_collate_clause(), used -** by the ".lint fkey-indexes" command. This scalar function is always -** called with four arguments - the parent table name, the parent column name, -** the child table name and the child column name. -** -** fkey_collate_clause('parent-tab', 'parent-col', 'child-tab', 'child-col') -** -** If either of the named tables or columns do not exist, this function -** returns an empty string. An empty string is also returned if both tables -** and columns exist but have the same default collation sequence. Or, -** if both exist but the default collation sequences are different, this -** function returns the string " COLLATE ", where -** is the default collation sequence of the parent column. -*/ -static void shellFkeyCollateClause( - sqlite3_context *pCtx, - int nVal, - sqlite3_value **apVal -){ - sqlite3 *db = sqlite3_context_db_handle(pCtx); - const char *zParent; - const char *zParentCol; - const char *zParentSeq; - const char *zChild; - const char *zChildCol; - const char *zChildSeq = 0; /* Initialize to avoid false-positive warning */ - int rc; - - assert( nVal==4 ); - zParent = (const char*)sqlite3_value_text(apVal[0]); - zParentCol = (const char*)sqlite3_value_text(apVal[1]); - zChild = (const char*)sqlite3_value_text(apVal[2]); - zChildCol = (const char*)sqlite3_value_text(apVal[3]); - - sqlite3_result_text(pCtx, "", -1, SQLITE_STATIC); - rc = sqlite3_table_column_metadata( - db, "main", zParent, zParentCol, 0, &zParentSeq, 0, 0, 0 - ); - if( rc==SQLITE_OK ){ - rc = sqlite3_table_column_metadata( - db, "main", zChild, zChildCol, 0, &zChildSeq, 0, 0, 0 - ); - } - - if( rc==SQLITE_OK && sqlite3_stricmp(zParentSeq, zChildSeq) ){ - char *z = sqlite3_mprintf(" COLLATE %s", zParentSeq); - sqlite3_result_text(pCtx, z, -1, SQLITE_TRANSIENT); - sqlite3_free(z); - } -} - - -/* -** The implementation of dot-command ".lint fkey-indexes". -*/ -static int lintFkeyIndexes( - ShellState *pState, /* Current shell tool state */ - char **azArg, /* Array of arguments passed to dot command */ - int nArg /* Number of entries in azArg[] */ -){ - sqlite3 *db = pState->db; /* Database handle to query "main" db of */ - FILE *out = pState->out; /* Stream to write non-error output to */ - int bVerbose = 0; /* If -verbose is present */ - int bGroupByParent = 0; /* If -groupbyparent is present */ - int i; /* To iterate through azArg[] */ - const char *zIndent = ""; /* How much to indent CREATE INDEX by */ - int rc; /* Return code */ - sqlite3_stmt *pSql = 0; /* Compiled version of SQL statement below */ - - /* - ** This SELECT statement returns one row for each foreign key constraint - ** in the schema of the main database. The column values are: - ** - ** 0. The text of an SQL statement similar to: - ** - ** "EXPLAIN QUERY PLAN SELECT 1 FROM child_table WHERE child_key=?" - ** - ** This SELECT is similar to the one that the foreign keys implementation - ** needs to run internally on child tables. If there is an index that can - ** be used to optimize this query, then it can also be used by the FK - ** implementation to optimize DELETE or UPDATE statements on the parent - ** table. - ** - ** 1. A GLOB pattern suitable for sqlite3_strglob(). If the plan output by - ** the EXPLAIN QUERY PLAN command matches this pattern, then the schema - ** contains an index that can be used to optimize the query. - ** - ** 2. Human readable text that describes the child table and columns. e.g. - ** - ** "child_table(child_key1, child_key2)" - ** - ** 3. Human readable text that describes the parent table and columns. e.g. - ** - ** "parent_table(parent_key1, parent_key2)" - ** - ** 4. A full CREATE INDEX statement for an index that could be used to - ** optimize DELETE or UPDATE statements on the parent table. e.g. - ** - ** "CREATE INDEX child_table_child_key ON child_table(child_key)" - ** - ** 5. The name of the parent table. - ** - ** These six values are used by the C logic below to generate the report. - */ - const char *zSql = - "SELECT " - " 'EXPLAIN QUERY PLAN SELECT 1 FROM ' || quote(s.name) || ' WHERE '" - " || group_concat(quote(s.name) || '.' || quote(f.[from]) || '=?' " - " || fkey_collate_clause(" - " f.[table], COALESCE(f.[to], p.[name]), s.name, f.[from]),' AND ')" - ", " - " 'SEARCH TABLE ' || s.name || ' USING COVERING INDEX*('" - " || group_concat('*=?', ' AND ') || ')'" - ", " - " s.name || '(' || group_concat(f.[from], ', ') || ')'" - ", " - " f.[table] || '(' || group_concat(COALESCE(f.[to], p.[name])) || ')'" - ", " - " 'CREATE INDEX ' || quote(s.name ||'_'|| group_concat(f.[from], '_'))" - " || ' ON ' || quote(s.name) || '('" - " || group_concat(quote(f.[from]) ||" - " fkey_collate_clause(" - " f.[table], COALESCE(f.[to], p.[name]), s.name, f.[from]), ', ')" - " || ');'" - ", " - " f.[table] " - "FROM sqlite_schema AS s, pragma_foreign_key_list(s.name) AS f " - "LEFT JOIN pragma_table_info AS p ON (pk-1=seq AND p.arg=f.[table]) " - "GROUP BY s.name, f.id " - "ORDER BY (CASE WHEN ? THEN f.[table] ELSE s.name END)" - ; - const char *zGlobIPK = "SEARCH TABLE * USING INTEGER PRIMARY KEY (rowid=?)"; - - for(i=2; i1 && sqlite3_strnicmp("-verbose", azArg[i], n)==0 ){ - bVerbose = 1; - } - else if( n>1 && sqlite3_strnicmp("-groupbyparent", azArg[i], n)==0 ){ - bGroupByParent = 1; - zIndent = " "; - } - else{ - raw_printf(stderr, "Usage: %s %s ?-verbose? ?-groupbyparent?\n", - azArg[0], azArg[1] - ); - return SQLITE_ERROR; - } - } - - /* Register the fkey_collate_clause() SQL function */ - rc = sqlite3_create_function(db, "fkey_collate_clause", 4, SQLITE_UTF8, - 0, shellFkeyCollateClause, 0, 0 - ); - - - if( rc==SQLITE_OK ){ - rc = sqlite3_prepare_v2(db, zSql, -1, &pSql, 0); - } - if( rc==SQLITE_OK ){ - sqlite3_bind_int(pSql, 1, bGroupByParent); - } - - if( rc==SQLITE_OK ){ - int rc2; - char *zPrev = 0; - while( SQLITE_ROW==sqlite3_step(pSql) ){ - int res = -1; - sqlite3_stmt *pExplain = 0; - const char *zEQP = (const char*)sqlite3_column_text(pSql, 0); - const char *zGlob = (const char*)sqlite3_column_text(pSql, 1); - const char *zFrom = (const char*)sqlite3_column_text(pSql, 2); - const char *zTarget = (const char*)sqlite3_column_text(pSql, 3); - const char *zCI = (const char*)sqlite3_column_text(pSql, 4); - const char *zParent = (const char*)sqlite3_column_text(pSql, 5); - - rc = sqlite3_prepare_v2(db, zEQP, -1, &pExplain, 0); - if( rc!=SQLITE_OK ) break; - if( SQLITE_ROW==sqlite3_step(pExplain) ){ - const char *zPlan = (const char*)sqlite3_column_text(pExplain, 3); - res = ( - 0==sqlite3_strglob(zGlob, zPlan) - || 0==sqlite3_strglob(zGlobIPK, zPlan) - ); - } - rc = sqlite3_finalize(pExplain); - if( rc!=SQLITE_OK ) break; - - if( res<0 ){ - raw_printf(stderr, "Error: internal error"); - break; - }else{ - if( bGroupByParent - && (bVerbose || res==0) - && (zPrev==0 || sqlite3_stricmp(zParent, zPrev)) - ){ - raw_printf(out, "-- Parent table %s\n", zParent); - sqlite3_free(zPrev); - zPrev = sqlite3_mprintf("%s", zParent); - } - - if( res==0 ){ - raw_printf(out, "%s%s --> %s\n", zIndent, zCI, zTarget); - }else if( bVerbose ){ - raw_printf(out, "%s/* no extra indexes required for %s -> %s */\n", - zIndent, zFrom, zTarget - ); - } - } - } - sqlite3_free(zPrev); - - if( rc!=SQLITE_OK ){ - raw_printf(stderr, "%s\n", sqlite3_errmsg(db)); - } - - rc2 = sqlite3_finalize(pSql); - if( rc==SQLITE_OK && rc2!=SQLITE_OK ){ - rc = rc2; - raw_printf(stderr, "%s\n", sqlite3_errmsg(db)); - } - }else{ - raw_printf(stderr, "%s\n", sqlite3_errmsg(db)); - } - - return rc; -} - -/* -** Implementation of ".lint" dot command. -*/ -static int lintDotCommand( - ShellState *pState, /* Current shell tool state */ - char **azArg, /* Array of arguments passed to dot command */ - int nArg /* Number of entries in azArg[] */ -){ - int n; - n = (nArg>=2 ? strlen30(azArg[1]) : 0); - if( n<1 || sqlite3_strnicmp(azArg[1], "fkey-indexes", n) ) goto usage; - return lintFkeyIndexes(pState, azArg, nArg); - - usage: - raw_printf(stderr, "Usage %s sub-command ?switches...?\n", azArg[0]); - raw_printf(stderr, "Where sub-commands are:\n"); - raw_printf(stderr, " fkey-indexes\n"); - return SQLITE_ERROR; -} - -#if !defined SQLITE_OMIT_VIRTUALTABLE -static void shellPrepare( - sqlite3 *db, - int *pRc, - const char *zSql, - sqlite3_stmt **ppStmt -){ - *ppStmt = 0; - if( *pRc==SQLITE_OK ){ - int rc = sqlite3_prepare_v2(db, zSql, -1, ppStmt, 0); - if( rc!=SQLITE_OK ){ - raw_printf(stderr, "sql error: %s (%d)\n", - sqlite3_errmsg(db), sqlite3_errcode(db) - ); - *pRc = rc; - } - } -} - -/* -** Create a prepared statement using printf-style arguments for the SQL. -** -** This routine is could be marked "static". But it is not always used, -** depending on compile-time options. By omitting the "static", we avoid -** nuisance compiler warnings about "defined but not used". -*/ -void shellPreparePrintf( - sqlite3 *db, - int *pRc, - sqlite3_stmt **ppStmt, - const char *zFmt, - ... -){ - *ppStmt = 0; - if( *pRc==SQLITE_OK ){ - va_list ap; - char *z; - va_start(ap, zFmt); - z = sqlite3_vmprintf(zFmt, ap); - va_end(ap); - if( z==0 ){ - *pRc = SQLITE_NOMEM; - }else{ - shellPrepare(db, pRc, z, ppStmt); - sqlite3_free(z); - } - } -} - -/* Finalize the prepared statement created using shellPreparePrintf(). -** -** This routine is could be marked "static". But it is not always used, -** depending on compile-time options. By omitting the "static", we avoid -** nuisance compiler warnings about "defined but not used". -*/ -void shellFinalize( - int *pRc, - sqlite3_stmt *pStmt -){ - if( pStmt ){ - sqlite3 *db = sqlite3_db_handle(pStmt); - int rc = sqlite3_finalize(pStmt); - if( *pRc==SQLITE_OK ){ - if( rc!=SQLITE_OK ){ - raw_printf(stderr, "SQL error: %s\n", sqlite3_errmsg(db)); - } - *pRc = rc; - } - } -} - -/* Reset the prepared statement created using shellPreparePrintf(). -** -** This routine is could be marked "static". But it is not always used, -** depending on compile-time options. By omitting the "static", we avoid -** nuisance compiler warnings about "defined but not used". -*/ -void shellReset( - int *pRc, - sqlite3_stmt *pStmt -){ - int rc = sqlite3_reset(pStmt); - if( *pRc==SQLITE_OK ){ - if( rc!=SQLITE_OK ){ - sqlite3 *db = sqlite3_db_handle(pStmt); - raw_printf(stderr, "SQL error: %s\n", sqlite3_errmsg(db)); - } - *pRc = rc; - } -} -#endif /* !defined SQLITE_OMIT_VIRTUALTABLE */ - -#if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_HAVE_ZLIB) -/****************************************************************************** -** The ".archive" or ".ar" command. -*/ -/* -** Structure representing a single ".ar" command. -*/ -typedef struct ArCommand ArCommand; -struct ArCommand { - u8 eCmd; /* An AR_CMD_* value */ - u8 bVerbose; /* True if --verbose */ - u8 bZip; /* True if the archive is a ZIP */ - u8 bDryRun; /* True if --dry-run */ - u8 bAppend; /* True if --append */ - u8 fromCmdLine; /* Run from -A instead of .archive */ - int nArg; /* Number of command arguments */ - char *zSrcTable; /* "sqlar", "zipfile($file)" or "zip" */ - const char *zFile; /* --file argument, or NULL */ - const char *zDir; /* --directory argument, or NULL */ - char **azArg; /* Array of command arguments */ - ShellState *p; /* Shell state */ - sqlite3 *db; /* Database containing the archive */ -}; - -/* -** Print a usage message for the .ar command to stderr and return SQLITE_ERROR. -*/ -static int arUsage(FILE *f){ - showHelp(f,"archive"); - return SQLITE_ERROR; -} - -/* -** Print an error message for the .ar command to stderr and return -** SQLITE_ERROR. -*/ -static int arErrorMsg(ArCommand *pAr, const char *zFmt, ...){ - va_list ap; - char *z; - va_start(ap, zFmt); - z = sqlite3_vmprintf(zFmt, ap); - va_end(ap); - utf8_printf(stderr, "Error: %s\n", z); - if( pAr->fromCmdLine ){ - utf8_printf(stderr, "Use \"-A\" for more help\n"); - }else{ - utf8_printf(stderr, "Use \".archive --help\" for more help\n"); - } - sqlite3_free(z); - return SQLITE_ERROR; -} - -/* -** Values for ArCommand.eCmd. -*/ -#define AR_CMD_CREATE 1 -#define AR_CMD_UPDATE 2 -#define AR_CMD_INSERT 3 -#define AR_CMD_EXTRACT 4 -#define AR_CMD_LIST 5 -#define AR_CMD_HELP 6 - -/* -** Other (non-command) switches. -*/ -#define AR_SWITCH_VERBOSE 7 -#define AR_SWITCH_FILE 8 -#define AR_SWITCH_DIRECTORY 9 -#define AR_SWITCH_APPEND 10 -#define AR_SWITCH_DRYRUN 11 - -static int arProcessSwitch(ArCommand *pAr, int eSwitch, const char *zArg){ - switch( eSwitch ){ - case AR_CMD_CREATE: - case AR_CMD_EXTRACT: - case AR_CMD_LIST: - case AR_CMD_UPDATE: - case AR_CMD_INSERT: - case AR_CMD_HELP: - if( pAr->eCmd ){ - return arErrorMsg(pAr, "multiple command options"); - } - pAr->eCmd = eSwitch; - break; - - case AR_SWITCH_DRYRUN: - pAr->bDryRun = 1; - break; - case AR_SWITCH_VERBOSE: - pAr->bVerbose = 1; - break; - case AR_SWITCH_APPEND: - pAr->bAppend = 1; - /* Fall thru into --file */ - case AR_SWITCH_FILE: - pAr->zFile = zArg; - break; - case AR_SWITCH_DIRECTORY: - pAr->zDir = zArg; - break; - } - - return SQLITE_OK; -} - -/* -** Parse the command line for an ".ar" command. The results are written into -** structure (*pAr). SQLITE_OK is returned if the command line is parsed -** successfully, otherwise an error message is written to stderr and -** SQLITE_ERROR returned. -*/ -static int arParseCommand( - char **azArg, /* Array of arguments passed to dot command */ - int nArg, /* Number of entries in azArg[] */ - ArCommand *pAr /* Populate this object */ -){ - struct ArSwitch { - const char *zLong; - char cShort; - u8 eSwitch; - u8 bArg; - } aSwitch[] = { - { "create", 'c', AR_CMD_CREATE, 0 }, - { "extract", 'x', AR_CMD_EXTRACT, 0 }, - { "insert", 'i', AR_CMD_INSERT, 0 }, - { "list", 't', AR_CMD_LIST, 0 }, - { "update", 'u', AR_CMD_UPDATE, 0 }, - { "help", 'h', AR_CMD_HELP, 0 }, - { "verbose", 'v', AR_SWITCH_VERBOSE, 0 }, - { "file", 'f', AR_SWITCH_FILE, 1 }, - { "append", 'a', AR_SWITCH_APPEND, 1 }, - { "directory", 'C', AR_SWITCH_DIRECTORY, 1 }, - { "dryrun", 'n', AR_SWITCH_DRYRUN, 0 }, - }; - int nSwitch = sizeof(aSwitch) / sizeof(struct ArSwitch); - struct ArSwitch *pEnd = &aSwitch[nSwitch]; - - if( nArg<=1 ){ - utf8_printf(stderr, "Wrong number of arguments. Usage:\n"); - return arUsage(stderr); - }else{ - char *z = azArg[1]; - if( z[0]!='-' ){ - /* Traditional style [tar] invocation */ - int i; - int iArg = 2; - for(i=0; z[i]; i++){ - const char *zArg = 0; - struct ArSwitch *pOpt; - for(pOpt=&aSwitch[0]; pOptcShort ) break; - } - if( pOpt==pEnd ){ - return arErrorMsg(pAr, "unrecognized option: %c", z[i]); - } - if( pOpt->bArg ){ - if( iArg>=nArg ){ - return arErrorMsg(pAr, "option requires an argument: %c",z[i]); - } - zArg = azArg[iArg++]; - } - if( arProcessSwitch(pAr, pOpt->eSwitch, zArg) ) return SQLITE_ERROR; - } - pAr->nArg = nArg-iArg; - if( pAr->nArg>0 ){ - pAr->azArg = &azArg[iArg]; - } - }else{ - /* Non-traditional invocation */ - int iArg; - for(iArg=1; iArgazArg = &azArg[iArg]; - pAr->nArg = nArg-iArg; - break; - } - n = strlen30(z); - - if( z[1]!='-' ){ - int i; - /* One or more short options */ - for(i=1; icShort ) break; - } - if( pOpt==pEnd ){ - return arErrorMsg(pAr, "unrecognized option: %c", z[i]); - } - if( pOpt->bArg ){ - if( i<(n-1) ){ - zArg = &z[i+1]; - i = n; - }else{ - if( iArg>=(nArg-1) ){ - return arErrorMsg(pAr, "option requires an argument: %c", - z[i]); - } - zArg = azArg[++iArg]; - } - } - if( arProcessSwitch(pAr, pOpt->eSwitch, zArg) ) return SQLITE_ERROR; - } - }else if( z[2]=='\0' ){ - /* A -- option, indicating that all remaining command line words - ** are command arguments. */ - pAr->azArg = &azArg[iArg+1]; - pAr->nArg = nArg-iArg-1; - break; - }else{ - /* A long option */ - const char *zArg = 0; /* Argument for option, if any */ - struct ArSwitch *pMatch = 0; /* Matching option */ - struct ArSwitch *pOpt; /* Iterator */ - for(pOpt=&aSwitch[0]; pOptzLong; - if( (n-2)<=strlen30(zLong) && 0==memcmp(&z[2], zLong, n-2) ){ - if( pMatch ){ - return arErrorMsg(pAr, "ambiguous option: %s",z); - }else{ - pMatch = pOpt; - } - } - } - - if( pMatch==0 ){ - return arErrorMsg(pAr, "unrecognized option: %s", z); - } - if( pMatch->bArg ){ - if( iArg>=(nArg-1) ){ - return arErrorMsg(pAr, "option requires an argument: %s", z); - } - zArg = azArg[++iArg]; - } - if( arProcessSwitch(pAr, pMatch->eSwitch, zArg) ) return SQLITE_ERROR; - } - } - } - } - - return SQLITE_OK; -} - -/* -** This function assumes that all arguments within the ArCommand.azArg[] -** array refer to archive members, as for the --extract or --list commands. -** It checks that each of them are present. If any specified file is not -** present in the archive, an error is printed to stderr and an error -** code returned. Otherwise, if all specified arguments are present in -** the archive, SQLITE_OK is returned. -** -** This function strips any trailing '/' characters from each argument. -** This is consistent with the way the [tar] command seems to work on -** Linux. -*/ -static int arCheckEntries(ArCommand *pAr){ - int rc = SQLITE_OK; - if( pAr->nArg ){ - int i, j; - sqlite3_stmt *pTest = 0; - - shellPreparePrintf(pAr->db, &rc, &pTest, - "SELECT name FROM %s WHERE name=$name", - pAr->zSrcTable - ); - j = sqlite3_bind_parameter_index(pTest, "$name"); - for(i=0; inArg && rc==SQLITE_OK; i++){ - char *z = pAr->azArg[i]; - int n = strlen30(z); - int bOk = 0; - while( n>0 && z[n-1]=='/' ) n--; - z[n] = '\0'; - sqlite3_bind_text(pTest, j, z, -1, SQLITE_STATIC); - if( SQLITE_ROW==sqlite3_step(pTest) ){ - bOk = 1; - } - shellReset(&rc, pTest); - if( rc==SQLITE_OK && bOk==0 ){ - utf8_printf(stderr, "not found in archive: %s\n", z); - rc = SQLITE_ERROR; - } - } - shellFinalize(&rc, pTest); - } - return rc; -} - -/* -** Format a WHERE clause that can be used against the "sqlar" table to -** identify all archive members that match the command arguments held -** in (*pAr). Leave this WHERE clause in (*pzWhere) before returning. -** The caller is responsible for eventually calling sqlite3_free() on -** any non-NULL (*pzWhere) value. -*/ -static void arWhereClause( - int *pRc, - ArCommand *pAr, - char **pzWhere /* OUT: New WHERE clause */ -){ - char *zWhere = 0; - if( *pRc==SQLITE_OK ){ - if( pAr->nArg==0 ){ - zWhere = sqlite3_mprintf("1"); - }else{ - int i; - const char *zSep = ""; - for(i=0; inArg; i++){ - const char *z = pAr->azArg[i]; - zWhere = sqlite3_mprintf( - "%z%s name = '%q' OR substr(name,1,%d) = '%q/'", - zWhere, zSep, z, strlen30(z)+1, z - ); - if( zWhere==0 ){ - *pRc = SQLITE_NOMEM; - break; - } - zSep = " OR "; - } - } - } - *pzWhere = zWhere; -} - -/* -** Implementation of .ar "lisT" command. -*/ -static int arListCommand(ArCommand *pAr){ - const char *zSql = "SELECT %s FROM %s WHERE %s"; - const char *azCols[] = { - "name", - "lsmode(mode), sz, datetime(mtime, 'unixepoch'), name" - }; - - char *zWhere = 0; - sqlite3_stmt *pSql = 0; - int rc; - - rc = arCheckEntries(pAr); - arWhereClause(&rc, pAr, &zWhere); - - shellPreparePrintf(pAr->db, &rc, &pSql, zSql, azCols[pAr->bVerbose], - pAr->zSrcTable, zWhere); - if( pAr->bDryRun ){ - utf8_printf(pAr->p->out, "%s\n", sqlite3_sql(pSql)); - }else{ - while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pSql) ){ - if( pAr->bVerbose ){ - utf8_printf(pAr->p->out, "%s % 10d %s %s\n", - sqlite3_column_text(pSql, 0), - sqlite3_column_int(pSql, 1), - sqlite3_column_text(pSql, 2), - sqlite3_column_text(pSql, 3) - ); - }else{ - utf8_printf(pAr->p->out, "%s\n", sqlite3_column_text(pSql, 0)); - } - } - } - shellFinalize(&rc, pSql); - sqlite3_free(zWhere); - return rc; -} - - -/* -** Implementation of .ar "eXtract" command. -*/ -static int arExtractCommand(ArCommand *pAr){ - const char *zSql1 = - "SELECT " - " ($dir || name)," - " writefile(($dir || name), %s, mode, mtime) " - "FROM %s WHERE (%s) AND (data IS NULL OR $dirOnly = 0)" - " AND name NOT GLOB '*..[/\\]*'"; - - const char *azExtraArg[] = { - "sqlar_uncompress(data, sz)", - "data" - }; - - sqlite3_stmt *pSql = 0; - int rc = SQLITE_OK; - char *zDir = 0; - char *zWhere = 0; - int i, j; - - /* If arguments are specified, check that they actually exist within - ** the archive before proceeding. And formulate a WHERE clause to - ** match them. */ - rc = arCheckEntries(pAr); - arWhereClause(&rc, pAr, &zWhere); - - if( rc==SQLITE_OK ){ - if( pAr->zDir ){ - zDir = sqlite3_mprintf("%s/", pAr->zDir); - }else{ - zDir = sqlite3_mprintf(""); - } - if( zDir==0 ) rc = SQLITE_NOMEM; - } - - shellPreparePrintf(pAr->db, &rc, &pSql, zSql1, - azExtraArg[pAr->bZip], pAr->zSrcTable, zWhere - ); - - if( rc==SQLITE_OK ){ - j = sqlite3_bind_parameter_index(pSql, "$dir"); - sqlite3_bind_text(pSql, j, zDir, -1, SQLITE_STATIC); - - /* Run the SELECT statement twice. The first time, writefile() is called - ** for all archive members that should be extracted. The second time, - ** only for the directories. This is because the timestamps for - ** extracted directories must be reset after they are populated (as - ** populating them changes the timestamp). */ - for(i=0; i<2; i++){ - j = sqlite3_bind_parameter_index(pSql, "$dirOnly"); - sqlite3_bind_int(pSql, j, i); - if( pAr->bDryRun ){ - utf8_printf(pAr->p->out, "%s\n", sqlite3_sql(pSql)); - }else{ - while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pSql) ){ - if( i==0 && pAr->bVerbose ){ - utf8_printf(pAr->p->out, "%s\n", sqlite3_column_text(pSql, 0)); - } - } - } - shellReset(&rc, pSql); - } - shellFinalize(&rc, pSql); - } - - sqlite3_free(zDir); - sqlite3_free(zWhere); - return rc; -} - -/* -** Run the SQL statement in zSql. Or if doing a --dryrun, merely print it out. -*/ -static int arExecSql(ArCommand *pAr, const char *zSql){ - int rc; - if( pAr->bDryRun ){ - utf8_printf(pAr->p->out, "%s\n", zSql); - rc = SQLITE_OK; - }else{ - char *zErr = 0; - rc = sqlite3_exec(pAr->db, zSql, 0, 0, &zErr); - if( zErr ){ - utf8_printf(stdout, "ERROR: %s\n", zErr); - sqlite3_free(zErr); - } - } - return rc; -} - - -/* -** Implementation of .ar "create", "insert", and "update" commands. -** -** create -> Create a new SQL archive -** insert -> Insert or reinsert all files listed -** update -> Insert files that have changed or that were not -** previously in the archive -** -** Create the "sqlar" table in the database if it does not already exist. -** Then add each file in the azFile[] array to the archive. Directories -** are added recursively. If argument bVerbose is non-zero, a message is -** printed on stdout for each file archived. -** -** The create command is the same as update, except that it drops -** any existing "sqlar" table before beginning. The "insert" command -** always overwrites every file named on the command-line, where as -** "update" only overwrites if the size or mtime or mode has changed. -*/ -static int arCreateOrUpdateCommand( - ArCommand *pAr, /* Command arguments and options */ - int bUpdate, /* true for a --create. */ - int bOnlyIfChanged /* Only update if file has changed */ -){ - const char *zCreate = - "CREATE TABLE IF NOT EXISTS sqlar(\n" - " name TEXT PRIMARY KEY, -- name of the file\n" - " mode INT, -- access permissions\n" - " mtime INT, -- last modification time\n" - " sz INT, -- original file size\n" - " data BLOB -- compressed content\n" - ")"; - const char *zDrop = "DROP TABLE IF EXISTS sqlar"; - const char *zInsertFmt[2] = { - "REPLACE INTO %s(name,mode,mtime,sz,data)\n" - " SELECT\n" - " %s,\n" - " mode,\n" - " mtime,\n" - " CASE substr(lsmode(mode),1,1)\n" - " WHEN '-' THEN length(data)\n" - " WHEN 'd' THEN 0\n" - " ELSE -1 END,\n" - " sqlar_compress(data)\n" - " FROM fsdir(%Q,%Q) AS disk\n" - " WHERE lsmode(mode) NOT LIKE '?%%'%s;" - , - "REPLACE INTO %s(name,mode,mtime,data)\n" - " SELECT\n" - " %s,\n" - " mode,\n" - " mtime,\n" - " data\n" - " FROM fsdir(%Q,%Q) AS disk\n" - " WHERE lsmode(mode) NOT LIKE '?%%'%s;" - }; - int i; /* For iterating through azFile[] */ - int rc; /* Return code */ - const char *zTab = 0; /* SQL table into which to insert */ - char *zSql; - char zTemp[50]; - char *zExists = 0; - - arExecSql(pAr, "PRAGMA page_size=512"); - rc = arExecSql(pAr, "SAVEPOINT ar;"); - if( rc!=SQLITE_OK ) return rc; - zTemp[0] = 0; - if( pAr->bZip ){ - /* Initialize the zipfile virtual table, if necessary */ - if( pAr->zFile ){ - sqlite3_uint64 r; - sqlite3_randomness(sizeof(r),&r); - sqlite3_snprintf(sizeof(zTemp),zTemp,"zip%016llx",r); - zTab = zTemp; - zSql = sqlite3_mprintf( - "CREATE VIRTUAL TABLE temp.%s USING zipfile(%Q)", - zTab, pAr->zFile - ); - rc = arExecSql(pAr, zSql); - sqlite3_free(zSql); - }else{ - zTab = "zip"; - } - }else{ - /* Initialize the table for an SQLAR */ - zTab = "sqlar"; - if( bUpdate==0 ){ - rc = arExecSql(pAr, zDrop); - if( rc!=SQLITE_OK ) goto end_ar_transaction; - } - rc = arExecSql(pAr, zCreate); - } - if( bOnlyIfChanged ){ - zExists = sqlite3_mprintf( - " AND NOT EXISTS(" - "SELECT 1 FROM %s AS mem" - " WHERE mem.name=disk.name" - " AND mem.mtime=disk.mtime" - " AND mem.mode=disk.mode)", zTab); - }else{ - zExists = sqlite3_mprintf(""); - } - if( zExists==0 ) rc = SQLITE_NOMEM; - for(i=0; inArg && rc==SQLITE_OK; i++){ - char *zSql2 = sqlite3_mprintf(zInsertFmt[pAr->bZip], zTab, - pAr->bVerbose ? "shell_putsnl(name)" : "name", - pAr->azArg[i], pAr->zDir, zExists); - rc = arExecSql(pAr, zSql2); - sqlite3_free(zSql2); - } -end_ar_transaction: - if( rc!=SQLITE_OK ){ - sqlite3_exec(pAr->db, "ROLLBACK TO ar; RELEASE ar;", 0, 0, 0); - }else{ - rc = arExecSql(pAr, "RELEASE ar;"); - if( pAr->bZip && pAr->zFile ){ - zSql = sqlite3_mprintf("DROP TABLE %s", zTemp); - arExecSql(pAr, zSql); - sqlite3_free(zSql); - } - } - sqlite3_free(zExists); - return rc; -} - -/* -** Implementation of ".ar" dot command. -*/ -static int arDotCommand( - ShellState *pState, /* Current shell tool state */ - int fromCmdLine, /* True if -A command-line option, not .ar cmd */ - char **azArg, /* Array of arguments passed to dot command */ - int nArg /* Number of entries in azArg[] */ -){ - ArCommand cmd; - int rc; - memset(&cmd, 0, sizeof(cmd)); - cmd.fromCmdLine = fromCmdLine; - rc = arParseCommand(azArg, nArg, &cmd); - if( rc==SQLITE_OK ){ - int eDbType = SHELL_OPEN_UNSPEC; - cmd.p = pState; - cmd.db = pState->db; - if( cmd.zFile ){ - eDbType = deduceDatabaseType(cmd.zFile, 1); - }else{ - eDbType = pState->openMode; - } - if( eDbType==SHELL_OPEN_ZIPFILE ){ - if( cmd.eCmd==AR_CMD_EXTRACT || cmd.eCmd==AR_CMD_LIST ){ - if( cmd.zFile==0 ){ - cmd.zSrcTable = sqlite3_mprintf("zip"); - }else{ - cmd.zSrcTable = sqlite3_mprintf("zipfile(%Q)", cmd.zFile); - } - } - cmd.bZip = 1; - }else if( cmd.zFile ){ - int flags; - if( cmd.bAppend ) eDbType = SHELL_OPEN_APPENDVFS; - if( cmd.eCmd==AR_CMD_CREATE || cmd.eCmd==AR_CMD_INSERT - || cmd.eCmd==AR_CMD_UPDATE ){ - flags = SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE; - }else{ - flags = SQLITE_OPEN_READONLY; - } - cmd.db = 0; - if( cmd.bDryRun ){ - utf8_printf(pState->out, "-- open database '%s'%s\n", cmd.zFile, - eDbType==SHELL_OPEN_APPENDVFS ? " using 'apndvfs'" : ""); - } - rc = sqlite3_open_v2(cmd.zFile, &cmd.db, flags, - eDbType==SHELL_OPEN_APPENDVFS ? "apndvfs" : 0); - if( rc!=SQLITE_OK ){ - utf8_printf(stderr, "cannot open file: %s (%s)\n", - cmd.zFile, sqlite3_errmsg(cmd.db) - ); - goto end_ar_command; - } - sqlite3_fileio_init(cmd.db, 0, 0); - sqlite3_sqlar_init(cmd.db, 0, 0); - sqlite3_create_function(cmd.db, "shell_putsnl", 1, SQLITE_UTF8, cmd.p, - shellPutsFunc, 0, 0); - - } - if( cmd.zSrcTable==0 && cmd.bZip==0 && cmd.eCmd!=AR_CMD_HELP ){ - if( cmd.eCmd!=AR_CMD_CREATE - && sqlite3_table_column_metadata(cmd.db,0,"sqlar","name",0,0,0,0,0) - ){ - utf8_printf(stderr, "database does not contain an 'sqlar' table\n"); - rc = SQLITE_ERROR; - goto end_ar_command; - } - cmd.zSrcTable = sqlite3_mprintf("sqlar"); - } - - switch( cmd.eCmd ){ - case AR_CMD_CREATE: - rc = arCreateOrUpdateCommand(&cmd, 0, 0); - break; - - case AR_CMD_EXTRACT: - rc = arExtractCommand(&cmd); - break; - - case AR_CMD_LIST: - rc = arListCommand(&cmd); - break; - - case AR_CMD_HELP: - arUsage(pState->out); - break; - - case AR_CMD_INSERT: - rc = arCreateOrUpdateCommand(&cmd, 1, 0); - break; - - default: - assert( cmd.eCmd==AR_CMD_UPDATE ); - rc = arCreateOrUpdateCommand(&cmd, 1, 1); - break; - } - } -end_ar_command: - if( cmd.db!=pState->db ){ - close_db(cmd.db); - } - sqlite3_free(cmd.zSrcTable); - - return rc; -} -/* End of the ".archive" or ".ar" command logic -*******************************************************************************/ -#endif /* !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_HAVE_ZLIB) */ - -#if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_ENABLE_DBPAGE_VTAB) -/* -** If (*pRc) is not SQLITE_OK when this function is called, it is a no-op. -** Otherwise, the SQL statement or statements in zSql are executed using -** database connection db and the error code written to *pRc before -** this function returns. -*/ -static void shellExec(sqlite3 *db, int *pRc, const char *zSql){ - int rc = *pRc; - if( rc==SQLITE_OK ){ - char *zErr = 0; - rc = sqlite3_exec(db, zSql, 0, 0, &zErr); - if( rc!=SQLITE_OK ){ - raw_printf(stderr, "SQL error: %s\n", zErr); - } - *pRc = rc; - } -} - -/* -** Like shellExec(), except that zFmt is a printf() style format string. -*/ -static void shellExecPrintf(sqlite3 *db, int *pRc, const char *zFmt, ...){ - char *z = 0; - if( *pRc==SQLITE_OK ){ - va_list ap; - va_start(ap, zFmt); - z = sqlite3_vmprintf(zFmt, ap); - va_end(ap); - if( z==0 ){ - *pRc = SQLITE_NOMEM; - }else{ - shellExec(db, pRc, z); - } - sqlite3_free(z); - } -} - -/* -** If *pRc is not SQLITE_OK when this function is called, it is a no-op. -** Otherwise, an attempt is made to allocate, zero and return a pointer -** to a buffer nByte bytes in size. If an OOM error occurs, *pRc is set -** to SQLITE_NOMEM and NULL returned. -*/ -static void *shellMalloc(int *pRc, sqlite3_int64 nByte){ - void *pRet = 0; - if( *pRc==SQLITE_OK ){ - pRet = sqlite3_malloc64(nByte); - if( pRet==0 ){ - *pRc = SQLITE_NOMEM; - }else{ - memset(pRet, 0, nByte); - } - } - return pRet; -} - -/* -** If *pRc is not SQLITE_OK when this function is called, it is a no-op. -** Otherwise, zFmt is treated as a printf() style string. The result of -** formatting it along with any trailing arguments is written into a -** buffer obtained from sqlite3_malloc(), and pointer to which is returned. -** It is the responsibility of the caller to eventually free this buffer -** using a call to sqlite3_free(). -** -** If an OOM error occurs, (*pRc) is set to SQLITE_NOMEM and a NULL -** pointer returned. -*/ -static char *shellMPrintf(int *pRc, const char *zFmt, ...){ - char *z = 0; - if( *pRc==SQLITE_OK ){ - va_list ap; - va_start(ap, zFmt); - z = sqlite3_vmprintf(zFmt, ap); - va_end(ap); - if( z==0 ){ - *pRc = SQLITE_NOMEM; - } - } - return z; -} - -/* -** When running the ".recover" command, each output table, and the special -** orphaned row table if it is required, is represented by an instance -** of the following struct. -*/ -typedef struct RecoverTable RecoverTable; -struct RecoverTable { - char *zQuoted; /* Quoted version of table name */ - int nCol; /* Number of columns in table */ - char **azlCol; /* Array of column lists */ - int iPk; /* Index of IPK column */ -}; - -/* -** Free a RecoverTable object allocated by recoverFindTable() or -** recoverOrphanTable(). -*/ -static void recoverFreeTable(RecoverTable *pTab){ - if( pTab ){ - sqlite3_free(pTab->zQuoted); - if( pTab->azlCol ){ - int i; - for(i=0; i<=pTab->nCol; i++){ - sqlite3_free(pTab->azlCol[i]); - } - sqlite3_free(pTab->azlCol); - } - sqlite3_free(pTab); - } -} - -/* -** This function is a no-op if (*pRc) is not SQLITE_OK when it is called. -** Otherwise, it allocates and returns a RecoverTable object based on the -** final four arguments passed to this function. It is the responsibility -** of the caller to eventually free the returned object using -** recoverFreeTable(). -*/ -static RecoverTable *recoverNewTable( - int *pRc, /* IN/OUT: Error code */ - const char *zName, /* Name of table */ - const char *zSql, /* CREATE TABLE statement */ - int bIntkey, - int nCol -){ - sqlite3 *dbtmp = 0; /* sqlite3 handle for testing CREATE TABLE */ - int rc = *pRc; - RecoverTable *pTab = 0; - - pTab = (RecoverTable*)shellMalloc(&rc, sizeof(RecoverTable)); - if( rc==SQLITE_OK ){ - int nSqlCol = 0; - int bSqlIntkey = 0; - sqlite3_stmt *pStmt = 0; - - rc = sqlite3_open("", &dbtmp); - if( rc==SQLITE_OK ){ - sqlite3_create_function(dbtmp, "shell_idquote", 1, SQLITE_UTF8, 0, - shellIdQuote, 0, 0); - } - if( rc==SQLITE_OK ){ - rc = sqlite3_exec(dbtmp, "PRAGMA writable_schema = on", 0, 0, 0); - } - if( rc==SQLITE_OK ){ - rc = sqlite3_exec(dbtmp, zSql, 0, 0, 0); - if( rc==SQLITE_ERROR ){ - rc = SQLITE_OK; - goto finished; - } - } - shellPreparePrintf(dbtmp, &rc, &pStmt, - "SELECT count(*) FROM pragma_table_info(%Q)", zName - ); - if( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){ - nSqlCol = sqlite3_column_int(pStmt, 0); - } - shellFinalize(&rc, pStmt); - - if( rc!=SQLITE_OK || nSqlColiPk to the index - ** of the column, where columns are 0-numbered from left to right. - ** Or, if this is a WITHOUT ROWID table or if there is no IPK column, - ** leave zPk as "_rowid_" and pTab->iPk at -2. */ - pTab->iPk = -2; - if( bIntkey ){ - shellPreparePrintf(dbtmp, &rc, &pPkFinder, - "SELECT cid, name FROM pragma_table_info(%Q) " - " WHERE pk=1 AND type='integer' COLLATE nocase" - " AND NOT EXISTS (SELECT cid FROM pragma_table_info(%Q) WHERE pk=2)" - , zName, zName - ); - if( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pPkFinder) ){ - pTab->iPk = sqlite3_column_int(pPkFinder, 0); - zPk = (const char*)sqlite3_column_text(pPkFinder, 1); - } - } - - pTab->zQuoted = shellMPrintf(&rc, "\"%w\"", zName); - pTab->azlCol = (char**)shellMalloc(&rc, sizeof(char*) * (nSqlCol+1)); - pTab->nCol = nSqlCol; - - if( bIntkey ){ - pTab->azlCol[0] = shellMPrintf(&rc, "\"%w\"", zPk); - }else{ - pTab->azlCol[0] = shellMPrintf(&rc, ""); - } - i = 1; - shellPreparePrintf(dbtmp, &rc, &pStmt, - "SELECT %Q || group_concat(shell_idquote(name), ', ') " - " FILTER (WHERE cid!=%d) OVER (ORDER BY %s cid) " - "FROM pragma_table_info(%Q)", - bIntkey ? ", " : "", pTab->iPk, - bIntkey ? "" : "(CASE WHEN pk=0 THEN 1000000 ELSE pk END), ", - zName - ); - while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){ - const char *zText = (const char*)sqlite3_column_text(pStmt, 0); - pTab->azlCol[i] = shellMPrintf(&rc, "%s%s", pTab->azlCol[0], zText); - i++; - } - shellFinalize(&rc, pStmt); - - shellFinalize(&rc, pPkFinder); - } - } - - finished: - sqlite3_close(dbtmp); - *pRc = rc; - if( rc!=SQLITE_OK || (pTab && pTab->zQuoted==0) ){ - recoverFreeTable(pTab); - pTab = 0; - } - return pTab; -} - -/* -** This function is called to search the schema recovered from the -** sqlite_schema table of the (possibly) corrupt database as part -** of a ".recover" command. Specifically, for a table with root page -** iRoot and at least nCol columns. Additionally, if bIntkey is 0, the -** table must be a WITHOUT ROWID table, or if non-zero, not one of -** those. -** -** If a table is found, a (RecoverTable*) object is returned. Or, if -** no such table is found, but bIntkey is false and iRoot is the -** root page of an index in the recovered schema, then (*pbNoop) is -** set to true and NULL returned. Or, if there is no such table or -** index, NULL is returned and (*pbNoop) set to 0, indicating that -** the caller should write data to the orphans table. -*/ -static RecoverTable *recoverFindTable( - ShellState *pState, /* Shell state object */ - int *pRc, /* IN/OUT: Error code */ - int iRoot, /* Root page of table */ - int bIntkey, /* True for an intkey table */ - int nCol, /* Number of columns in table */ - int *pbNoop /* OUT: True if iRoot is root of index */ -){ - sqlite3_stmt *pStmt = 0; - RecoverTable *pRet = 0; - int bNoop = 0; - const char *zSql = 0; - const char *zName = 0; - - /* Search the recovered schema for an object with root page iRoot. */ - shellPreparePrintf(pState->db, pRc, &pStmt, - "SELECT type, name, sql FROM recovery.schema WHERE rootpage=%d", iRoot - ); - while( *pRc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){ - const char *zType = (const char*)sqlite3_column_text(pStmt, 0); - if( bIntkey==0 && sqlite3_stricmp(zType, "index")==0 ){ - bNoop = 1; - break; - } - if( sqlite3_stricmp(zType, "table")==0 ){ - zName = (const char*)sqlite3_column_text(pStmt, 1); - zSql = (const char*)sqlite3_column_text(pStmt, 2); - pRet = recoverNewTable(pRc, zName, zSql, bIntkey, nCol); - break; - } - } - - shellFinalize(pRc, pStmt); - *pbNoop = bNoop; - return pRet; -} - -/* -** Return a RecoverTable object representing the orphans table. -*/ -static RecoverTable *recoverOrphanTable( - ShellState *pState, /* Shell state object */ - int *pRc, /* IN/OUT: Error code */ - const char *zLostAndFound, /* Base name for orphans table */ - int nCol /* Number of user data columns */ -){ - RecoverTable *pTab = 0; - if( nCol>=0 && *pRc==SQLITE_OK ){ - int i; - - /* This block determines the name of the orphan table. The prefered - ** name is zLostAndFound. But if that clashes with another name - ** in the recovered schema, try zLostAndFound_0, zLostAndFound_1 - ** and so on until a non-clashing name is found. */ - int iTab = 0; - char *zTab = shellMPrintf(pRc, "%s", zLostAndFound); - sqlite3_stmt *pTest = 0; - shellPrepare(pState->db, pRc, - "SELECT 1 FROM recovery.schema WHERE name=?", &pTest - ); - if( pTest ) sqlite3_bind_text(pTest, 1, zTab, -1, SQLITE_TRANSIENT); - while( *pRc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pTest) ){ - shellReset(pRc, pTest); - sqlite3_free(zTab); - zTab = shellMPrintf(pRc, "%s_%d", zLostAndFound, iTab++); - sqlite3_bind_text(pTest, 1, zTab, -1, SQLITE_TRANSIENT); - } - shellFinalize(pRc, pTest); - - pTab = (RecoverTable*)shellMalloc(pRc, sizeof(RecoverTable)); - if( pTab ){ - pTab->zQuoted = shellMPrintf(pRc, "\"%w\"", zTab); - pTab->nCol = nCol; - pTab->iPk = -2; - if( nCol>0 ){ - pTab->azlCol = (char**)shellMalloc(pRc, sizeof(char*) * (nCol+1)); - if( pTab->azlCol ){ - pTab->azlCol[nCol] = shellMPrintf(pRc, ""); - for(i=nCol-1; i>=0; i--){ - pTab->azlCol[i] = shellMPrintf(pRc, "%s, NULL", pTab->azlCol[i+1]); - } - } - } - - if( *pRc!=SQLITE_OK ){ - recoverFreeTable(pTab); - pTab = 0; - }else{ - raw_printf(pState->out, - "CREATE TABLE %s(rootpgno INTEGER, " - "pgno INTEGER, nfield INTEGER, id INTEGER", pTab->zQuoted - ); - for(i=0; iout, ", c%d", i); - } - raw_printf(pState->out, ");\n"); - } - } - sqlite3_free(zTab); - } - return pTab; -} - -/* -** This function is called to recover data from the database. A script -** to construct a new database containing all recovered data is output -** on stream pState->out. -*/ -static int recoverDatabaseCmd(ShellState *pState, int nArg, char **azArg){ - int rc = SQLITE_OK; - sqlite3_stmt *pLoop = 0; /* Loop through all root pages */ - sqlite3_stmt *pPages = 0; /* Loop through all pages in a group */ - sqlite3_stmt *pCells = 0; /* Loop through all cells in a page */ - const char *zRecoveryDb = ""; /* Name of "recovery" database */ - const char *zLostAndFound = "lost_and_found"; - int i; - int nOrphan = -1; - RecoverTable *pOrphan = 0; - - int bFreelist = 1; /* 0 if --freelist-corrupt is specified */ - int bRowids = 1; /* 0 if --no-rowids */ - for(i=1; iout, azArg[0]); - return 1; - } - } - - shellExecPrintf(pState->db, &rc, - /* Attach an in-memory database named 'recovery'. Create an indexed - ** cache of the sqlite_dbptr virtual table. */ - "PRAGMA writable_schema = on;" - "ATTACH %Q AS recovery;" - "DROP TABLE IF EXISTS recovery.dbptr;" - "DROP TABLE IF EXISTS recovery.freelist;" - "DROP TABLE IF EXISTS recovery.map;" - "DROP TABLE IF EXISTS recovery.schema;" - "CREATE TABLE recovery.freelist(pgno INTEGER PRIMARY KEY);", zRecoveryDb - ); - - if( bFreelist ){ - shellExec(pState->db, &rc, - "WITH trunk(pgno) AS (" - " SELECT shell_int32(" - " (SELECT data FROM sqlite_dbpage WHERE pgno=1), 8) AS x " - " WHERE x>0" - " UNION" - " SELECT shell_int32(" - " (SELECT data FROM sqlite_dbpage WHERE pgno=trunk.pgno), 0) AS x " - " FROM trunk WHERE x>0" - ")," - "freelist(data, n, freepgno) AS (" - " SELECT data, min(16384, shell_int32(data, 1)-1), t.pgno " - " FROM trunk t, sqlite_dbpage s WHERE s.pgno=t.pgno" - " UNION ALL" - " SELECT data, n-1, shell_int32(data, 2+n) " - " FROM freelist WHERE n>=0" - ")" - "REPLACE INTO recovery.freelist SELECT freepgno FROM freelist;" - ); - } - - /* If this is an auto-vacuum database, add all pointer-map pages to - ** the freelist table. Do this regardless of whether or not - ** --freelist-corrupt was specified. */ - shellExec(pState->db, &rc, - "WITH ptrmap(pgno) AS (" - " SELECT 2 WHERE shell_int32(" - " (SELECT data FROM sqlite_dbpage WHERE pgno=1), 13" - " )" - " UNION ALL " - " SELECT pgno+1+(SELECT page_size FROM pragma_page_size)/5 AS pp " - " FROM ptrmap WHERE pp<=(SELECT page_count FROM pragma_page_count)" - ")" - "REPLACE INTO recovery.freelist SELECT pgno FROM ptrmap" - ); - - shellExec(pState->db, &rc, - "CREATE TABLE recovery.dbptr(" - " pgno, child, PRIMARY KEY(child, pgno)" - ") WITHOUT ROWID;" - "INSERT OR IGNORE INTO recovery.dbptr(pgno, child) " - " SELECT * FROM sqlite_dbptr" - " WHERE pgno NOT IN freelist AND child NOT IN freelist;" - - /* Delete any pointer to page 1. This ensures that page 1 is considered - ** a root page, regardless of how corrupt the db is. */ - "DELETE FROM recovery.dbptr WHERE child = 1;" - - /* Delete all pointers to any pages that have more than one pointer - ** to them. Such pages will be treated as root pages when recovering - ** data. */ - "DELETE FROM recovery.dbptr WHERE child IN (" - " SELECT child FROM recovery.dbptr GROUP BY child HAVING count(*)>1" - ");" - - /* Create the "map" table that will (eventually) contain instructions - ** for dealing with each page in the db that contains one or more - ** records. */ - "CREATE TABLE recovery.map(" - "pgno INTEGER PRIMARY KEY, maxlen INT, intkey, root INT" - ");" - - /* Populate table [map]. If there are circular loops of pages in the - ** database, the following adds all pages in such a loop to the map - ** as individual root pages. This could be handled better. */ - "WITH pages(i, maxlen) AS (" - " SELECT page_count, (" - " SELECT max(field+1) FROM sqlite_dbdata WHERE pgno=page_count" - " ) FROM pragma_page_count WHERE page_count>0" - " UNION ALL" - " SELECT i-1, (" - " SELECT max(field+1) FROM sqlite_dbdata WHERE pgno=i-1" - " ) FROM pages WHERE i>=2" - ")" - "INSERT INTO recovery.map(pgno, maxlen, intkey, root) " - " SELECT i, maxlen, NULL, (" - " WITH p(orig, pgno, parent) AS (" - " SELECT 0, i, (SELECT pgno FROM recovery.dbptr WHERE child=i)" - " UNION " - " SELECT i, p.parent, " - " (SELECT pgno FROM recovery.dbptr WHERE child=p.parent) FROM p" - " )" - " SELECT pgno FROM p WHERE (parent IS NULL OR pgno = orig)" - ") " - "FROM pages WHERE maxlen IS NOT NULL AND i NOT IN freelist;" - "UPDATE recovery.map AS o SET intkey = (" - " SELECT substr(data, 1, 1)==X'0D' FROM sqlite_dbpage WHERE pgno=o.pgno" - ");" - - /* Extract data from page 1 and any linked pages into table - ** recovery.schema. With the same schema as an sqlite_schema table. */ - "CREATE TABLE recovery.schema(type, name, tbl_name, rootpage, sql);" - "INSERT INTO recovery.schema SELECT " - " max(CASE WHEN field=0 THEN value ELSE NULL END)," - " max(CASE WHEN field=1 THEN value ELSE NULL END)," - " max(CASE WHEN field=2 THEN value ELSE NULL END)," - " max(CASE WHEN field=3 THEN value ELSE NULL END)," - " max(CASE WHEN field=4 THEN value ELSE NULL END)" - "FROM sqlite_dbdata WHERE pgno IN (" - " SELECT pgno FROM recovery.map WHERE root=1" - ")" - "GROUP BY pgno, cell;" - "CREATE INDEX recovery.schema_rootpage ON schema(rootpage);" - ); - - /* Open a transaction, then print out all non-virtual, non-"sqlite_%" - ** CREATE TABLE statements that extracted from the existing schema. */ - if( rc==SQLITE_OK ){ - sqlite3_stmt *pStmt = 0; - /* ".recover" might output content in an order which causes immediate - ** foreign key constraints to be violated. So disable foreign-key - ** constraint enforcement to prevent problems when running the output - ** script. */ - raw_printf(pState->out, "PRAGMA foreign_keys=OFF;\n"); - raw_printf(pState->out, "BEGIN;\n"); - raw_printf(pState->out, "PRAGMA writable_schema = on;\n"); - shellPrepare(pState->db, &rc, - "SELECT sql FROM recovery.schema " - "WHERE type='table' AND sql LIKE 'create table%'", &pStmt - ); - while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){ - const char *zCreateTable = (const char*)sqlite3_column_text(pStmt, 0); - raw_printf(pState->out, "CREATE TABLE IF NOT EXISTS %s;\n", - &zCreateTable[12] - ); - } - shellFinalize(&rc, pStmt); - } - - /* Figure out if an orphan table will be required. And if so, how many - ** user columns it should contain */ - shellPrepare(pState->db, &rc, - "SELECT coalesce(max(maxlen), -2) FROM recovery.map WHERE root>1" - , &pLoop - ); - if( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pLoop) ){ - nOrphan = sqlite3_column_int(pLoop, 0); - } - shellFinalize(&rc, pLoop); - pLoop = 0; - - shellPrepare(pState->db, &rc, - "SELECT pgno FROM recovery.map WHERE root=?", &pPages - ); - - shellPrepare(pState->db, &rc, - "SELECT max(field), group_concat(shell_escape_crnl(quote" - "(case when (? AND field<0) then NULL else value end)" - "), ', ')" - ", min(field) " - "FROM sqlite_dbdata WHERE pgno = ? AND field != ?" - "GROUP BY cell", &pCells - ); - - /* Loop through each root page. */ - shellPrepare(pState->db, &rc, - "SELECT root, intkey, max(maxlen) FROM recovery.map" - " WHERE root>1 GROUP BY root, intkey ORDER BY root=(" - " SELECT rootpage FROM recovery.schema WHERE name='sqlite_sequence'" - ")", &pLoop - ); - while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pLoop) ){ - int iRoot = sqlite3_column_int(pLoop, 0); - int bIntkey = sqlite3_column_int(pLoop, 1); - int nCol = sqlite3_column_int(pLoop, 2); - int bNoop = 0; - RecoverTable *pTab; - - assert( bIntkey==0 || bIntkey==1 ); - pTab = recoverFindTable(pState, &rc, iRoot, bIntkey, nCol, &bNoop); - if( bNoop || rc ) continue; - if( pTab==0 ){ - if( pOrphan==0 ){ - pOrphan = recoverOrphanTable(pState, &rc, zLostAndFound, nOrphan); - } - pTab = pOrphan; - if( pTab==0 ) break; - } - - if( 0==sqlite3_stricmp(pTab->zQuoted, "\"sqlite_sequence\"") ){ - raw_printf(pState->out, "DELETE FROM sqlite_sequence;\n"); - } - sqlite3_bind_int(pPages, 1, iRoot); - if( bRowids==0 && pTab->iPk<0 ){ - sqlite3_bind_int(pCells, 1, 1); - }else{ - sqlite3_bind_int(pCells, 1, 0); - } - sqlite3_bind_int(pCells, 3, pTab->iPk); - - while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pPages) ){ - int iPgno = sqlite3_column_int(pPages, 0); - sqlite3_bind_int(pCells, 2, iPgno); - while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pCells) ){ - int nField = sqlite3_column_int(pCells, 0); - int iMin = sqlite3_column_int(pCells, 2); - const char *zVal = (const char*)sqlite3_column_text(pCells, 1); - - RecoverTable *pTab2 = pTab; - if( pTab!=pOrphan && (iMin<0)!=bIntkey ){ - if( pOrphan==0 ){ - pOrphan = recoverOrphanTable(pState, &rc, zLostAndFound, nOrphan); - } - pTab2 = pOrphan; - if( pTab2==0 ) break; - } - - nField = nField+1; - if( pTab2==pOrphan ){ - raw_printf(pState->out, - "INSERT INTO %s VALUES(%d, %d, %d, %s%s%s);\n", - pTab2->zQuoted, iRoot, iPgno, nField, - iMin<0 ? "" : "NULL, ", zVal, pTab2->azlCol[nField] - ); - }else{ - raw_printf(pState->out, "INSERT INTO %s(%s) VALUES( %s );\n", - pTab2->zQuoted, pTab2->azlCol[nField], zVal - ); - } - } - shellReset(&rc, pCells); - } - shellReset(&rc, pPages); - if( pTab!=pOrphan ) recoverFreeTable(pTab); - } - shellFinalize(&rc, pLoop); - shellFinalize(&rc, pPages); - shellFinalize(&rc, pCells); - recoverFreeTable(pOrphan); - - /* The rest of the schema */ - if( rc==SQLITE_OK ){ - sqlite3_stmt *pStmt = 0; - shellPrepare(pState->db, &rc, - "SELECT sql, name FROM recovery.schema " - "WHERE sql NOT LIKE 'create table%'", &pStmt - ); - while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){ - const char *zSql = (const char*)sqlite3_column_text(pStmt, 0); - if( sqlite3_strnicmp(zSql, "create virt", 11)==0 ){ - const char *zName = (const char*)sqlite3_column_text(pStmt, 1); - char *zPrint = shellMPrintf(&rc, - "INSERT INTO sqlite_schema VALUES('table', %Q, %Q, 0, %Q)", - zName, zName, zSql - ); - raw_printf(pState->out, "%s;\n", zPrint); - sqlite3_free(zPrint); - }else{ - raw_printf(pState->out, "%s;\n", zSql); - } - } - shellFinalize(&rc, pStmt); - } - - if( rc==SQLITE_OK ){ - raw_printf(pState->out, "PRAGMA writable_schema = off;\n"); - raw_printf(pState->out, "COMMIT;\n"); - } - sqlite3_exec(pState->db, "DETACH recovery", 0, 0, 0); - return rc; -} -#endif /* !(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_ENABLE_DBPAGE_VTAB) */ - - -/* -** If an input line begins with "." then invoke this routine to -** process that line. -** -** Return 1 on error, 2 to exit, and 0 otherwise. -*/ -static int do_meta_command(char *zLine, ShellState *p){ - int h = 1; - int nArg = 0; - int n, c; - int rc = 0; - char *azArg[52]; - -#ifndef SQLITE_OMIT_VIRTUALTABLE - if( p->expert.pExpert ){ - expertFinish(p, 1, 0); - } -#endif - - /* Parse the input line into tokens. - */ - while( zLine[h] && nArgdb, shellAuth, p); - }else{ - sqlite3_set_authorizer(p->db, 0, 0); - } - }else -#endif - -#if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_HAVE_ZLIB) - if( c=='a' && strncmp(azArg[0], "archive", n)==0 ){ - open_db(p, 0); - rc = arDotCommand(p, 0, azArg, nArg); - }else -#endif - - if( (c=='b' && n>=3 && strncmp(azArg[0], "backup", n)==0) - || (c=='s' && n>=3 && strncmp(azArg[0], "save", n)==0) - ){ - const char *zDestFile = 0; - const char *zDb = 0; - sqlite3 *pDest; - sqlite3_backup *pBackup; - int j; - int bAsync = 0; - const char *zVfs = 0; - for(j=1; jdb, zDb); - if( pBackup==0 ){ - utf8_printf(stderr, "Error: %s\n", sqlite3_errmsg(pDest)); - close_db(pDest); - return 1; - } - while( (rc = sqlite3_backup_step(pBackup,100))==SQLITE_OK ){} - sqlite3_backup_finish(pBackup); - if( rc==SQLITE_DONE ){ - rc = 0; - }else{ - utf8_printf(stderr, "Error: %s\n", sqlite3_errmsg(pDest)); - rc = 1; - } - close_db(pDest); - }else - - if( c=='b' && n>=3 && strncmp(azArg[0], "bail", n)==0 ){ - if( nArg==2 ){ - bail_on_error = booleanValue(azArg[1]); - }else{ - raw_printf(stderr, "Usage: .bail on|off\n"); - rc = 1; - } - }else - - if( c=='b' && n>=3 && strncmp(azArg[0], "binary", n)==0 ){ - if( nArg==2 ){ - if( booleanValue(azArg[1]) ){ - setBinaryMode(p->out, 1); - }else{ - setTextMode(p->out, 1); - } - }else{ - raw_printf(stderr, "Usage: .binary on|off\n"); - rc = 1; - } - }else - - if( c=='c' && strcmp(azArg[0],"cd")==0 ){ - if( nArg==2 ){ -#if defined(_WIN32) || defined(WIN32) - wchar_t *z = sqlite3_win32_utf8_to_unicode(azArg[1]); - rc = !SetCurrentDirectoryW(z); - sqlite3_free(z); -#else - rc = chdir(azArg[1]); -#endif - if( rc ){ - utf8_printf(stderr, "Cannot change to directory \"%s\"\n", azArg[1]); - rc = 1; - } - }else{ - raw_printf(stderr, "Usage: .cd DIRECTORY\n"); - rc = 1; - } - }else - - /* The undocumented ".breakpoint" command causes a call to the no-op - ** routine named test_breakpoint(). - */ - if( c=='b' && n>=3 && strncmp(azArg[0], "breakpoint", n)==0 ){ - test_breakpoint(); - }else - - if( c=='c' && n>=3 && strncmp(azArg[0], "changes", n)==0 ){ - if( nArg==2 ){ - setOrClearFlag(p, SHFLG_CountChanges, azArg[1]); - }else{ - raw_printf(stderr, "Usage: .changes on|off\n"); - rc = 1; - } - }else - - /* Cancel output redirection, if it is currently set (by .testcase) - ** Then read the content of the testcase-out.txt file and compare against - ** azArg[1]. If there are differences, report an error and exit. - */ - if( c=='c' && n>=3 && strncmp(azArg[0], "check", n)==0 ){ - char *zRes = 0; - output_reset(p); - if( nArg!=2 ){ - raw_printf(stderr, "Usage: .check GLOB-PATTERN\n"); - rc = 2; - }else if( (zRes = readFile("testcase-out.txt", 0))==0 ){ - raw_printf(stderr, "Error: cannot read 'testcase-out.txt'\n"); - rc = 2; - }else if( testcase_glob(azArg[1],zRes)==0 ){ - utf8_printf(stderr, - "testcase-%s FAILED\n Expected: [%s]\n Got: [%s]\n", - p->zTestcase, azArg[1], zRes); - rc = 1; - }else{ - utf8_printf(stdout, "testcase-%s ok\n", p->zTestcase); - p->nCheck++; - } - sqlite3_free(zRes); - }else - - if( c=='c' && strncmp(azArg[0], "clone", n)==0 ){ - if( nArg==2 ){ - tryToClone(p, azArg[1]); - }else{ - raw_printf(stderr, "Usage: .clone FILENAME\n"); - rc = 1; - } - }else - - if( c=='d' && n>1 && strncmp(azArg[0], "databases", n)==0 ){ - ShellState data; - char *zErrMsg = 0; - open_db(p, 0); - memcpy(&data, p, sizeof(data)); - data.showHeader = 0; - data.cMode = data.mode = MODE_List; - sqlite3_snprintf(sizeof(data.colSeparator),data.colSeparator,": "); - data.cnt = 0; - sqlite3_exec(p->db, "SELECT name, file FROM pragma_database_list", - callback, &data, &zErrMsg); - if( zErrMsg ){ - utf8_printf(stderr,"Error: %s\n", zErrMsg); - sqlite3_free(zErrMsg); - rc = 1; - } - }else - - if( c=='d' && n>=3 && strncmp(azArg[0], "dbconfig", n)==0 ){ - static const struct DbConfigChoices { - const char *zName; - int op; - } aDbConfig[] = { - { "defensive", SQLITE_DBCONFIG_DEFENSIVE }, - { "dqs_ddl", SQLITE_DBCONFIG_DQS_DDL }, - { "dqs_dml", SQLITE_DBCONFIG_DQS_DML }, - { "enable_fkey", SQLITE_DBCONFIG_ENABLE_FKEY }, - { "enable_qpsg", SQLITE_DBCONFIG_ENABLE_QPSG }, - { "enable_trigger", SQLITE_DBCONFIG_ENABLE_TRIGGER }, - { "enable_view", SQLITE_DBCONFIG_ENABLE_VIEW }, - { "fts3_tokenizer", SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER }, - { "legacy_alter_table", SQLITE_DBCONFIG_LEGACY_ALTER_TABLE }, - { "legacy_file_format", SQLITE_DBCONFIG_LEGACY_FILE_FORMAT }, - { "load_extension", SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION }, - { "no_ckpt_on_close", SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE }, - { "reset_database", SQLITE_DBCONFIG_RESET_DATABASE }, - { "trigger_eqp", SQLITE_DBCONFIG_TRIGGER_EQP }, - { "trusted_schema", SQLITE_DBCONFIG_TRUSTED_SCHEMA }, - { "writable_schema", SQLITE_DBCONFIG_WRITABLE_SCHEMA }, - }; - int ii, v; - open_db(p, 0); - for(ii=0; ii1 && strcmp(azArg[1], aDbConfig[ii].zName)!=0 ) continue; - if( nArg>=3 ){ - sqlite3_db_config(p->db, aDbConfig[ii].op, booleanValue(azArg[2]), 0); - } - sqlite3_db_config(p->db, aDbConfig[ii].op, -1, &v); - utf8_printf(p->out, "%19s %s\n", aDbConfig[ii].zName, v ? "on" : "off"); - if( nArg>1 ) break; - } - if( nArg>1 && ii==ArraySize(aDbConfig) ){ - utf8_printf(stderr, "Error: unknown dbconfig \"%s\"\n", azArg[1]); - utf8_printf(stderr, "Enter \".dbconfig\" with no arguments for a list\n"); - } - }else - - if( c=='d' && n>=3 && strncmp(azArg[0], "dbinfo", n)==0 ){ - rc = shell_dbinfo_command(p, nArg, azArg); - }else - -#if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_ENABLE_DBPAGE_VTAB) - if( c=='r' && strncmp(azArg[0], "recover", n)==0 ){ - open_db(p, 0); - rc = recoverDatabaseCmd(p, nArg, azArg); - }else -#endif /* !(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_ENABLE_DBPAGE_VTAB) */ - - if( c=='d' && strncmp(azArg[0], "dump", n)==0 ){ - char *zLike = 0; - char *zSql; - int i; - int savedShowHeader = p->showHeader; - int savedShellFlags = p->shellFlgs; - ShellClearFlag(p, SHFLG_PreserveRowid|SHFLG_Newlines|SHFLG_Echo); - for(i=1; iout, "PRAGMA foreign_keys=OFF;\n"); - raw_printf(p->out, "BEGIN TRANSACTION;\n"); - p->writableSchema = 0; - p->showHeader = 0; - /* Set writable_schema=ON since doing so forces SQLite to initialize - ** as much of the schema as it can even if the sqlite_schema table is - ** corrupt. */ - sqlite3_exec(p->db, "SAVEPOINT dump; PRAGMA writable_schema=ON", 0, 0, 0); - p->nErr = 0; - if( zLike==0 ) zLike = sqlite3_mprintf("true"); - zSql = sqlite3_mprintf( - "SELECT name, type, sql FROM sqlite_schema " - "WHERE (%s) AND type=='table'" - " AND sql NOT NULL" - " ORDER BY tbl_name='sqlite_sequence', rowid", - zLike - ); - run_schema_dump_query(p,zSql); - sqlite3_free(zSql); - zSql = sqlite3_mprintf( - "SELECT sql FROM sqlite_schema " - "WHERE (%s) AND sql NOT NULL" - " AND type IN ('index','trigger','view')", - zLike - ); - run_table_dump_query(p, zSql); - sqlite3_free(zSql); - sqlite3_free(zLike); - if( p->writableSchema ){ - raw_printf(p->out, "PRAGMA writable_schema=OFF;\n"); - p->writableSchema = 0; - } - sqlite3_exec(p->db, "PRAGMA writable_schema=OFF;", 0, 0, 0); - sqlite3_exec(p->db, "RELEASE dump;", 0, 0, 0); - raw_printf(p->out, p->nErr?"ROLLBACK; -- due to errors\n":"COMMIT;\n"); - p->showHeader = savedShowHeader; - p->shellFlgs = savedShellFlags; - }else - - if( c=='e' && strncmp(azArg[0], "echo", n)==0 ){ - if( nArg==2 ){ - setOrClearFlag(p, SHFLG_Echo, azArg[1]); - }else{ - raw_printf(stderr, "Usage: .echo on|off\n"); - rc = 1; - } - }else - - if( c=='e' && strncmp(azArg[0], "eqp", n)==0 ){ - if( nArg==2 ){ - p->autoEQPtest = 0; - if( p->autoEQPtrace ){ - if( p->db ) sqlite3_exec(p->db, "PRAGMA vdbe_trace=OFF;", 0, 0, 0); - p->autoEQPtrace = 0; - } - if( strcmp(azArg[1],"full")==0 ){ - p->autoEQP = AUTOEQP_full; - }else if( strcmp(azArg[1],"trigger")==0 ){ - p->autoEQP = AUTOEQP_trigger; -#ifdef SQLITE_DEBUG - }else if( strcmp(azArg[1],"test")==0 ){ - p->autoEQP = AUTOEQP_on; - p->autoEQPtest = 1; - }else if( strcmp(azArg[1],"trace")==0 ){ - p->autoEQP = AUTOEQP_full; - p->autoEQPtrace = 1; - open_db(p, 0); - sqlite3_exec(p->db, "SELECT name FROM sqlite_schema LIMIT 1", 0, 0, 0); - sqlite3_exec(p->db, "PRAGMA vdbe_trace=ON;", 0, 0, 0); -#endif - }else{ - p->autoEQP = (u8)booleanValue(azArg[1]); - } - }else{ - raw_printf(stderr, "Usage: .eqp off|on|trace|trigger|full\n"); - rc = 1; - } - }else - - if( c=='e' && strncmp(azArg[0], "exit", n)==0 ){ - if( nArg>1 && (rc = (int)integerValue(azArg[1]))!=0 ) exit(rc); - rc = 2; - }else - - /* The ".explain" command is automatic now. It is largely pointless. It - ** retained purely for backwards compatibility */ - if( c=='e' && strncmp(azArg[0], "explain", n)==0 ){ - int val = 1; - if( nArg>=2 ){ - if( strcmp(azArg[1],"auto")==0 ){ - val = 99; - }else{ - val = booleanValue(azArg[1]); - } - } - if( val==1 && p->mode!=MODE_Explain ){ - p->normalMode = p->mode; - p->mode = MODE_Explain; - p->autoExplain = 0; - }else if( val==0 ){ - if( p->mode==MODE_Explain ) p->mode = p->normalMode; - p->autoExplain = 0; - }else if( val==99 ){ - if( p->mode==MODE_Explain ) p->mode = p->normalMode; - p->autoExplain = 1; - } - }else - -#ifndef SQLITE_OMIT_VIRTUALTABLE - if( c=='e' && strncmp(azArg[0], "expert", n)==0 ){ - open_db(p, 0); - expertDotCommand(p, azArg, nArg); - }else -#endif - - if( c=='f' && strncmp(azArg[0], "filectrl", n)==0 ){ - static const struct { - const char *zCtrlName; /* Name of a test-control option */ - int ctrlCode; /* Integer code for that option */ - const char *zUsage; /* Usage notes */ - } aCtrl[] = { - { "size_limit", SQLITE_FCNTL_SIZE_LIMIT, "[LIMIT]" }, - { "chunk_size", SQLITE_FCNTL_CHUNK_SIZE, "SIZE" }, - /* { "win32_av_retry", SQLITE_FCNTL_WIN32_AV_RETRY, "COUNT DELAY" },*/ - { "persist_wal", SQLITE_FCNTL_PERSIST_WAL, "[BOOLEAN]" }, - { "psow", SQLITE_FCNTL_POWERSAFE_OVERWRITE, "[BOOLEAN]" }, - /* { "pragma", SQLITE_FCNTL_PRAGMA, "NAME ARG" },*/ - { "tempfilename", SQLITE_FCNTL_TEMPFILENAME, "" }, - { "has_moved", SQLITE_FCNTL_HAS_MOVED, "" }, - { "lock_timeout", SQLITE_FCNTL_LOCK_TIMEOUT, "MILLISEC" }, - { "reserve_bytes", SQLITE_FCNTL_RESERVE_BYTES, "[N]" }, - }; - int filectrl = -1; - int iCtrl = -1; - sqlite3_int64 iRes = 0; /* Integer result to display if rc2==1 */ - int isOk = 0; /* 0: usage 1: %lld 2: no-result */ - int n2, i; - const char *zCmd = 0; - const char *zSchema = 0; - - open_db(p, 0); - zCmd = nArg>=2 ? azArg[1] : "help"; - - if( zCmd[0]=='-' - && (strcmp(zCmd,"--schema")==0 || strcmp(zCmd,"-schema")==0) - && nArg>=4 - ){ - zSchema = azArg[2]; - for(i=3; iout, "Available file-controls:\n"); - for(i=0; iout, " .filectrl %s %s\n", - aCtrl[i].zCtrlName, aCtrl[i].zUsage); - } - rc = 1; - goto meta_command_exit; - } - - /* convert filectrl text option to value. allow any unique prefix - ** of the option name, or a numerical value. */ - n2 = strlen30(zCmd); - for(i=0; idb, zSchema, SQLITE_FCNTL_SIZE_LIMIT, &iRes); - isOk = 1; - break; - } - case SQLITE_FCNTL_LOCK_TIMEOUT: - case SQLITE_FCNTL_CHUNK_SIZE: { - int x; - if( nArg!=3 ) break; - x = (int)integerValue(azArg[2]); - sqlite3_file_control(p->db, zSchema, filectrl, &x); - isOk = 2; - break; - } - case SQLITE_FCNTL_PERSIST_WAL: - case SQLITE_FCNTL_POWERSAFE_OVERWRITE: { - int x; - if( nArg!=2 && nArg!=3 ) break; - x = nArg==3 ? booleanValue(azArg[2]) : -1; - sqlite3_file_control(p->db, zSchema, filectrl, &x); - iRes = x; - isOk = 1; - break; - } - case SQLITE_FCNTL_HAS_MOVED: { - int x; - if( nArg!=2 ) break; - sqlite3_file_control(p->db, zSchema, filectrl, &x); - iRes = x; - isOk = 1; - break; - } - case SQLITE_FCNTL_TEMPFILENAME: { - char *z = 0; - if( nArg!=2 ) break; - sqlite3_file_control(p->db, zSchema, filectrl, &z); - if( z ){ - utf8_printf(p->out, "%s\n", z); - sqlite3_free(z); - } - isOk = 2; - break; - } - case SQLITE_FCNTL_RESERVE_BYTES: { - int x; - if( nArg>=3 ){ - x = atoi(azArg[2]); - sqlite3_file_control(p->db, zSchema, filectrl, &x); - } - x = -1; - sqlite3_file_control(p->db, zSchema, filectrl, &x); - utf8_printf(p->out,"%d\n", x); - isOk = 2; - break; - } - } - } - if( isOk==0 && iCtrl>=0 ){ - utf8_printf(p->out, "Usage: .filectrl %s %s\n", zCmd,aCtrl[iCtrl].zUsage); - rc = 1; - }else if( isOk==1 ){ - char zBuf[100]; - sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld", iRes); - raw_printf(p->out, "%s\n", zBuf); - } - }else - - if( c=='f' && strncmp(azArg[0], "fullschema", n)==0 ){ - ShellState data; - char *zErrMsg = 0; - int doStats = 0; - memcpy(&data, p, sizeof(data)); - data.showHeader = 0; - data.cMode = data.mode = MODE_Semi; - if( nArg==2 && optionMatch(azArg[1], "indent") ){ - data.cMode = data.mode = MODE_Pretty; - nArg = 1; - } - if( nArg!=1 ){ - raw_printf(stderr, "Usage: .fullschema ?--indent?\n"); - rc = 1; - goto meta_command_exit; - } - open_db(p, 0); - rc = sqlite3_exec(p->db, - "SELECT sql FROM" - " (SELECT sql sql, type type, tbl_name tbl_name, name name, rowid x" - " FROM sqlite_schema UNION ALL" - " SELECT sql, type, tbl_name, name, rowid FROM sqlite_temp_schema) " - "WHERE type!='meta' AND sql NOTNULL AND name NOT LIKE 'sqlite_%' " - "ORDER BY rowid", - callback, &data, &zErrMsg - ); - if( rc==SQLITE_OK ){ - sqlite3_stmt *pStmt; - rc = sqlite3_prepare_v2(p->db, - "SELECT rowid FROM sqlite_schema" - " WHERE name GLOB 'sqlite_stat[134]'", - -1, &pStmt, 0); - doStats = sqlite3_step(pStmt)==SQLITE_ROW; - sqlite3_finalize(pStmt); - } - if( doStats==0 ){ - raw_printf(p->out, "/* No STAT tables available */\n"); - }else{ - raw_printf(p->out, "ANALYZE sqlite_schema;\n"); - sqlite3_exec(p->db, "SELECT 'ANALYZE sqlite_schema'", - callback, &data, &zErrMsg); - data.cMode = data.mode = MODE_Insert; - data.zDestTable = "sqlite_stat1"; - shell_exec(&data, "SELECT * FROM sqlite_stat1", &zErrMsg); - data.zDestTable = "sqlite_stat4"; - shell_exec(&data, "SELECT * FROM sqlite_stat4", &zErrMsg); - raw_printf(p->out, "ANALYZE sqlite_schema;\n"); - } - }else - - if( c=='h' && strncmp(azArg[0], "headers", n)==0 ){ - if( nArg==2 ){ - p->showHeader = booleanValue(azArg[1]); - p->shellFlgs |= SHFLG_HeaderSet; - }else{ - raw_printf(stderr, "Usage: .headers on|off\n"); - rc = 1; - } - }else - - if( c=='h' && strncmp(azArg[0], "help", n)==0 ){ - if( nArg>=2 ){ - n = showHelp(p->out, azArg[1]); - if( n==0 ){ - utf8_printf(p->out, "Nothing matches '%s'\n", azArg[1]); - } - }else{ - showHelp(p->out, 0); - } - }else - - if( c=='i' && strncmp(azArg[0], "import", n)==0 ){ - char *zTable = 0; /* Insert data into this table */ - char *zFile = 0; /* Name of file to extra content from */ - sqlite3_stmt *pStmt = NULL; /* A statement */ - int nCol; /* Number of columns in the table */ - int nByte; /* Number of bytes in an SQL string */ - int i, j; /* Loop counters */ - int needCommit; /* True to COMMIT or ROLLBACK at end */ - int nSep; /* Number of bytes in p->colSeparator[] */ - char *zSql; /* An SQL statement */ - ImportCtx sCtx; /* Reader context */ - char *(SQLITE_CDECL *xRead)(ImportCtx*); /* Func to read one value */ - int eVerbose = 0; /* Larger for more console output */ - int nSkip = 0; /* Initial lines to skip */ - int useOutputMode = 1; /* Use output mode to determine separators */ - - memset(&sCtx, 0, sizeof(sCtx)); - if( p->mode==MODE_Ascii ){ - xRead = ascii_read_one_field; - }else{ - xRead = csv_read_one_field; - } - for(i=1; iout, "ERROR: extra argument: \"%s\". Usage:\n", z); - showHelp(p->out, "import"); - rc = 1; - goto meta_command_exit; - } - }else if( strcmp(z,"-v")==0 ){ - eVerbose++; - }else if( strcmp(z,"-skip")==0 && iout, "ERROR: unknown option: \"%s\". Usage:\n", z); - showHelp(p->out, "import"); - rc = 1; - goto meta_command_exit; - } - } - if( zTable==0 ){ - utf8_printf(p->out, "ERROR: missing %s argument. Usage:\n", - zFile==0 ? "FILE" : "TABLE"); - showHelp(p->out, "import"); - rc = 1; - goto meta_command_exit; - } - seenInterrupt = 0; - open_db(p, 0); - if( useOutputMode ){ - /* If neither the --csv or --ascii options are specified, then set - ** the column and row separator characters from the output mode. */ - nSep = strlen30(p->colSeparator); - if( nSep==0 ){ - raw_printf(stderr, - "Error: non-null column separator required for import\n"); - rc = 1; - goto meta_command_exit; - } - if( nSep>1 ){ - raw_printf(stderr, - "Error: multi-character column separators not allowed" - " for import\n"); - rc = 1; - goto meta_command_exit; - } - nSep = strlen30(p->rowSeparator); - if( nSep==0 ){ - raw_printf(stderr, - "Error: non-null row separator required for import\n"); - rc = 1; - goto meta_command_exit; - } - if( nSep==2 && p->mode==MODE_Csv && strcmp(p->rowSeparator,SEP_CrLf)==0 ){ - /* When importing CSV (only), if the row separator is set to the - ** default output row separator, change it to the default input - ** row separator. This avoids having to maintain different input - ** and output row separators. */ - sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_Row); - nSep = strlen30(p->rowSeparator); - } - if( nSep>1 ){ - raw_printf(stderr, "Error: multi-character row separators not allowed" - " for import\n"); - rc = 1; - goto meta_command_exit; - } - sCtx.cColSep = p->colSeparator[0]; - sCtx.cRowSep = p->rowSeparator[0]; - } - sCtx.zFile = zFile; - sCtx.nLine = 1; - if( sCtx.zFile[0]=='|' ){ -#ifdef SQLITE_OMIT_POPEN - raw_printf(stderr, "Error: pipes are not supported in this OS\n"); - rc = 1; - goto meta_command_exit; -#else - sCtx.in = popen(sCtx.zFile+1, "r"); - sCtx.zFile = ""; - sCtx.xCloser = pclose; -#endif - }else{ - sCtx.in = fopen(sCtx.zFile, "rb"); - sCtx.xCloser = fclose; - } - if( sCtx.in==0 ){ - utf8_printf(stderr, "Error: cannot open \"%s\"\n", zFile); - rc = 1; - goto meta_command_exit; - } - if( eVerbose>=2 || (eVerbose>=1 && useOutputMode) ){ - char zSep[2]; - zSep[1] = 0; - zSep[0] = sCtx.cColSep; - utf8_printf(p->out, "Column separator "); - output_c_string(p->out, zSep); - utf8_printf(p->out, ", row separator "); - zSep[0] = sCtx.cRowSep; - output_c_string(p->out, zSep); - utf8_printf(p->out, "\n"); - } - while( (nSkip--)>0 ){ - while( xRead(&sCtx) && sCtx.cTerm==sCtx.cColSep ){} - } - zSql = sqlite3_mprintf("SELECT * FROM %s", zTable); - if( zSql==0 ){ - import_cleanup(&sCtx); - shell_out_of_memory(); - } - nByte = strlen30(zSql); - rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0); - import_append_char(&sCtx, 0); /* To ensure sCtx.z is allocated */ - if( rc && sqlite3_strglob("no such table: *", sqlite3_errmsg(p->db))==0 ){ - char *zCreate = sqlite3_mprintf("CREATE TABLE %s", zTable); - char cSep = '('; - while( xRead(&sCtx) ){ - zCreate = sqlite3_mprintf("%z%c\n \"%w\" TEXT", zCreate, cSep, sCtx.z); - cSep = ','; - if( sCtx.cTerm!=sCtx.cColSep ) break; - } - if( cSep=='(' ){ - sqlite3_free(zCreate); - import_cleanup(&sCtx); - utf8_printf(stderr,"%s: empty file\n", sCtx.zFile); - rc = 1; - goto meta_command_exit; - } - zCreate = sqlite3_mprintf("%z\n)", zCreate); - if( eVerbose>=1 ){ - utf8_printf(p->out, "%s\n", zCreate); - } - rc = sqlite3_exec(p->db, zCreate, 0, 0, 0); - sqlite3_free(zCreate); - if( rc ){ - utf8_printf(stderr, "CREATE TABLE %s(...) failed: %s\n", zTable, - sqlite3_errmsg(p->db)); - import_cleanup(&sCtx); - rc = 1; - goto meta_command_exit; - } - rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0); - } - sqlite3_free(zSql); - if( rc ){ - if (pStmt) sqlite3_finalize(pStmt); - utf8_printf(stderr,"Error: %s\n", sqlite3_errmsg(p->db)); - import_cleanup(&sCtx); - rc = 1; - goto meta_command_exit; - } - nCol = sqlite3_column_count(pStmt); - sqlite3_finalize(pStmt); - pStmt = 0; - if( nCol==0 ) return 0; /* no columns, no error */ - zSql = sqlite3_malloc64( nByte*2 + 20 + nCol*2 ); - if( zSql==0 ){ - import_cleanup(&sCtx); - shell_out_of_memory(); - } - sqlite3_snprintf(nByte+20, zSql, "INSERT INTO \"%w\" VALUES(?", zTable); - j = strlen30(zSql); - for(i=1; i=2 ){ - utf8_printf(p->out, "Insert using: %s\n", zSql); - } - rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0); - sqlite3_free(zSql); - if( rc ){ - utf8_printf(stderr, "Error: %s\n", sqlite3_errmsg(p->db)); - if (pStmt) sqlite3_finalize(pStmt); - import_cleanup(&sCtx); - rc = 1; - goto meta_command_exit; - } - needCommit = sqlite3_get_autocommit(p->db); - if( needCommit ) sqlite3_exec(p->db, "BEGIN", 0, 0, 0); - do{ - int startLine = sCtx.nLine; - for(i=0; imode==MODE_Ascii && (z==0 || z[0]==0) && i==0 ) break; - sqlite3_bind_text(pStmt, i+1, z, -1, SQLITE_TRANSIENT); - if( i=nCol ){ - sqlite3_step(pStmt); - rc = sqlite3_reset(pStmt); - if( rc!=SQLITE_OK ){ - utf8_printf(stderr, "%s:%d: INSERT failed: %s\n", sCtx.zFile, - startLine, sqlite3_errmsg(p->db)); - sCtx.nErr++; - }else{ - sCtx.nRow++; - } - } - }while( sCtx.cTerm!=EOF ); - - import_cleanup(&sCtx); - sqlite3_finalize(pStmt); - if( needCommit ) sqlite3_exec(p->db, "COMMIT", 0, 0, 0); - if( eVerbose>0 ){ - utf8_printf(p->out, - "Added %d rows with %d errors using %d lines of input\n", - sCtx.nRow, sCtx.nErr, sCtx.nLine-1); - } - }else - -#ifndef SQLITE_UNTESTABLE - if( c=='i' && strncmp(azArg[0], "imposter", n)==0 ){ - char *zSql; - char *zCollist = 0; - sqlite3_stmt *pStmt; - int tnum = 0; - int isWO = 0; /* True if making an imposter of a WITHOUT ROWID table */ - int lenPK = 0; /* Length of the PRIMARY KEY string for isWO tables */ - int i; - if( !(nArg==3 || (nArg==2 && sqlite3_stricmp(azArg[1],"off")==0)) ){ - utf8_printf(stderr, "Usage: .imposter INDEX IMPOSTER\n" - " .imposter off\n"); - /* Also allowed, but not documented: - ** - ** .imposter TABLE IMPOSTER - ** - ** where TABLE is a WITHOUT ROWID table. In that case, the - ** imposter is another WITHOUT ROWID table with the columns in - ** storage order. */ - rc = 1; - goto meta_command_exit; - } - open_db(p, 0); - if( nArg==2 ){ - sqlite3_test_control(SQLITE_TESTCTRL_IMPOSTER, p->db, "main", 0, 1); - goto meta_command_exit; - } - zSql = sqlite3_mprintf( - "SELECT rootpage, 0 FROM sqlite_schema" - " WHERE name='%q' AND type='index'" - "UNION ALL " - "SELECT rootpage, 1 FROM sqlite_schema" - " WHERE name='%q' AND type='table'" - " AND sql LIKE '%%without%%rowid%%'", - azArg[1], azArg[1] - ); - sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0); - sqlite3_free(zSql); - if( sqlite3_step(pStmt)==SQLITE_ROW ){ - tnum = sqlite3_column_int(pStmt, 0); - isWO = sqlite3_column_int(pStmt, 1); - } - sqlite3_finalize(pStmt); - zSql = sqlite3_mprintf("PRAGMA index_xinfo='%q'", azArg[1]); - rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0); - sqlite3_free(zSql); - i = 0; - while( sqlite3_step(pStmt)==SQLITE_ROW ){ - char zLabel[20]; - const char *zCol = (const char*)sqlite3_column_text(pStmt,2); - i++; - if( zCol==0 ){ - if( sqlite3_column_int(pStmt,1)==-1 ){ - zCol = "_ROWID_"; - }else{ - sqlite3_snprintf(sizeof(zLabel),zLabel,"expr%d",i); - zCol = zLabel; - } - } - if( isWO && lenPK==0 && sqlite3_column_int(pStmt,5)==0 && zCollist ){ - lenPK = (int)strlen(zCollist); - } - if( zCollist==0 ){ - zCollist = sqlite3_mprintf("\"%w\"", zCol); - }else{ - zCollist = sqlite3_mprintf("%z,\"%w\"", zCollist, zCol); - } - } - sqlite3_finalize(pStmt); - if( i==0 || tnum==0 ){ - utf8_printf(stderr, "no such index: \"%s\"\n", azArg[1]); - rc = 1; - sqlite3_free(zCollist); - goto meta_command_exit; - } - if( lenPK==0 ) lenPK = 100000; - zSql = sqlite3_mprintf( - "CREATE TABLE \"%w\"(%s,PRIMARY KEY(%.*s))WITHOUT ROWID", - azArg[2], zCollist, lenPK, zCollist); - sqlite3_free(zCollist); - rc = sqlite3_test_control(SQLITE_TESTCTRL_IMPOSTER, p->db, "main", 1, tnum); - if( rc==SQLITE_OK ){ - rc = sqlite3_exec(p->db, zSql, 0, 0, 0); - sqlite3_test_control(SQLITE_TESTCTRL_IMPOSTER, p->db, "main", 0, 0); - if( rc ){ - utf8_printf(stderr, "Error in [%s]: %s\n", zSql, sqlite3_errmsg(p->db)); - }else{ - utf8_printf(stdout, "%s;\n", zSql); - raw_printf(stdout, - "WARNING: writing to an imposter table will corrupt the \"%s\" %s!\n", - azArg[1], isWO ? "table" : "index" - ); - } - }else{ - raw_printf(stderr, "SQLITE_TESTCTRL_IMPOSTER returns %d\n", rc); - rc = 1; - } - sqlite3_free(zSql); - }else -#endif /* !defined(SQLITE_OMIT_TEST_CONTROL) */ - -#ifdef SQLITE_ENABLE_IOTRACE - if( c=='i' && strncmp(azArg[0], "iotrace", n)==0 ){ - SQLITE_API extern void (SQLITE_CDECL *sqlite3IoTrace)(const char*, ...); - if( iotrace && iotrace!=stdout ) fclose(iotrace); - iotrace = 0; - if( nArg<2 ){ - sqlite3IoTrace = 0; - }else if( strcmp(azArg[1], "-")==0 ){ - sqlite3IoTrace = iotracePrintf; - iotrace = stdout; - }else{ - iotrace = fopen(azArg[1], "w"); - if( iotrace==0 ){ - utf8_printf(stderr, "Error: cannot open \"%s\"\n", azArg[1]); - sqlite3IoTrace = 0; - rc = 1; - }else{ - sqlite3IoTrace = iotracePrintf; - } - } - }else -#endif - - if( c=='l' && n>=5 && strncmp(azArg[0], "limits", n)==0 ){ - static const struct { - const char *zLimitName; /* Name of a limit */ - int limitCode; /* Integer code for that limit */ - } aLimit[] = { - { "length", SQLITE_LIMIT_LENGTH }, - { "sql_length", SQLITE_LIMIT_SQL_LENGTH }, - { "column", SQLITE_LIMIT_COLUMN }, - { "expr_depth", SQLITE_LIMIT_EXPR_DEPTH }, - { "compound_select", SQLITE_LIMIT_COMPOUND_SELECT }, - { "vdbe_op", SQLITE_LIMIT_VDBE_OP }, - { "function_arg", SQLITE_LIMIT_FUNCTION_ARG }, - { "attached", SQLITE_LIMIT_ATTACHED }, - { "like_pattern_length", SQLITE_LIMIT_LIKE_PATTERN_LENGTH }, - { "variable_number", SQLITE_LIMIT_VARIABLE_NUMBER }, - { "trigger_depth", SQLITE_LIMIT_TRIGGER_DEPTH }, - { "worker_threads", SQLITE_LIMIT_WORKER_THREADS }, - }; - int i, n2; - open_db(p, 0); - if( nArg==1 ){ - for(i=0; idb, aLimit[i].limitCode, -1)); - } - }else if( nArg>3 ){ - raw_printf(stderr, "Usage: .limit NAME ?NEW-VALUE?\n"); - rc = 1; - goto meta_command_exit; - }else{ - int iLimit = -1; - n2 = strlen30(azArg[1]); - for(i=0; idb, aLimit[iLimit].limitCode, - (int)integerValue(azArg[2])); - } - printf("%20s %d\n", aLimit[iLimit].zLimitName, - sqlite3_limit(p->db, aLimit[iLimit].limitCode, -1)); - } - }else - - if( c=='l' && n>2 && strncmp(azArg[0], "lint", n)==0 ){ - open_db(p, 0); - lintDotCommand(p, azArg, nArg); - }else - -#ifndef SQLITE_OMIT_LOAD_EXTENSION - if( c=='l' && strncmp(azArg[0], "load", n)==0 ){ - const char *zFile, *zProc; - char *zErrMsg = 0; - if( nArg<2 ){ - raw_printf(stderr, "Usage: .load FILE ?ENTRYPOINT?\n"); - rc = 1; - goto meta_command_exit; - } - zFile = azArg[1]; - zProc = nArg>=3 ? azArg[2] : 0; - open_db(p, 0); - rc = sqlite3_load_extension(p->db, zFile, zProc, &zErrMsg); - if( rc!=SQLITE_OK ){ - utf8_printf(stderr, "Error: %s\n", zErrMsg); - sqlite3_free(zErrMsg); - rc = 1; - } - }else -#endif - - if( c=='l' && strncmp(azArg[0], "log", n)==0 ){ - if( nArg!=2 ){ - raw_printf(stderr, "Usage: .log FILENAME\n"); - rc = 1; - }else{ - const char *zFile = azArg[1]; - output_file_close(p->pLog); - p->pLog = output_file_open(zFile, 0); - } - }else - - if( c=='m' && strncmp(azArg[0], "mode", n)==0 ){ - const char *zMode = nArg>=2 ? azArg[1] : ""; - int n2 = strlen30(zMode); - int c2 = zMode[0]; - if( c2=='l' && n2>2 && strncmp(azArg[1],"lines",n2)==0 ){ - p->mode = MODE_Line; - sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_Row); - }else if( c2=='c' && strncmp(azArg[1],"columns",n2)==0 ){ - p->mode = MODE_Column; - if( (p->shellFlgs & SHFLG_HeaderSet)==0 ){ - p->showHeader = 1; - } - sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_Row); - }else if( c2=='l' && n2>2 && strncmp(azArg[1],"list",n2)==0 ){ - p->mode = MODE_List; - sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, SEP_Column); - sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_Row); - }else if( c2=='h' && strncmp(azArg[1],"html",n2)==0 ){ - p->mode = MODE_Html; - }else if( c2=='t' && strncmp(azArg[1],"tcl",n2)==0 ){ - p->mode = MODE_Tcl; - sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, SEP_Space); - sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_Row); - }else if( c2=='c' && strncmp(azArg[1],"csv",n2)==0 ){ - p->mode = MODE_Csv; - sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, SEP_Comma); - sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_CrLf); - }else if( c2=='t' && strncmp(azArg[1],"tabs",n2)==0 ){ - p->mode = MODE_List; - sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, SEP_Tab); - }else if( c2=='i' && strncmp(azArg[1],"insert",n2)==0 ){ - p->mode = MODE_Insert; - set_table_name(p, nArg>=3 ? azArg[2] : "table"); - }else if( c2=='q' && strncmp(azArg[1],"quote",n2)==0 ){ - p->mode = MODE_Quote; - sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, SEP_Comma); - sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_Row); - }else if( c2=='a' && strncmp(azArg[1],"ascii",n2)==0 ){ - p->mode = MODE_Ascii; - sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, SEP_Unit); - sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_Record); - }else if( c2=='m' && strncmp(azArg[1],"markdown",n2)==0 ){ - p->mode = MODE_Markdown; - }else if( c2=='t' && strncmp(azArg[1],"table",n2)==0 ){ - p->mode = MODE_Table; - }else if( c2=='b' && strncmp(azArg[1],"box",n2)==0 ){ - p->mode = MODE_Box; - }else if( c2=='j' && strncmp(azArg[1],"json",n2)==0 ){ - p->mode = MODE_Json; - }else if( nArg==1 ){ - raw_printf(p->out, "current output mode: %s\n", modeDescr[p->mode]); - }else{ - raw_printf(stderr, "Error: mode should be one of: " - "ascii box column csv html insert json line list markdown " - "quote table tabs tcl\n"); - rc = 1; - } - p->cMode = p->mode; - }else - - if( c=='n' && strncmp(azArg[0], "nullvalue", n)==0 ){ - if( nArg==2 ){ - sqlite3_snprintf(sizeof(p->nullValue), p->nullValue, - "%.*s", (int)ArraySize(p->nullValue)-1, azArg[1]); - }else{ - raw_printf(stderr, "Usage: .nullvalue STRING\n"); - rc = 1; - } - }else - -#ifdef SQLITE_DEBUG - if( c=='o' && strcmp(azArg[0],"oom")==0 ){ - int i; - for(i=1; iout, "missing argument on \"%s\"\n", azArg[i]); - rc = 1; - }else{ - oomRepeat = (int)integerValue(azArg[++i]); - } - }else if( IsDigit(z[0]) ){ - oomCounter = (int)integerValue(azArg[i]); - }else{ - raw_printf(p->out, "unknown argument: \"%s\"\n", azArg[i]); - raw_printf(p->out, "Usage: .oom [--repeat N] [M]\n"); - rc = 1; - } - } - if( rc==0 ){ - raw_printf(p->out, "oomCounter = %d\n", oomCounter); - raw_printf(p->out, "oomRepeat = %d\n", oomRepeat); - } - }else -#endif /* SQLITE_DEBUG */ - - if( c=='o' && strncmp(azArg[0], "open", n)==0 && n>=2 ){ - char *zNewFilename; /* Name of the database file to open */ - int iName = 1; /* Index in azArg[] of the filename */ - int newFlag = 0; /* True to delete file before opening */ - /* Close the existing database */ - session_close_all(p); - close_db(p->db); - p->db = 0; - p->zDbFilename = 0; - sqlite3_free(p->zFreeOnClose); - p->zFreeOnClose = 0; - p->openMode = SHELL_OPEN_UNSPEC; - p->openFlags = 0; - p->szMax = 0; - /* Check for command-line arguments */ - for(iName=1; iNameopenMode = SHELL_OPEN_ZIPFILE; -#endif - }else if( optionMatch(z, "append") ){ - p->openMode = SHELL_OPEN_APPENDVFS; - }else if( optionMatch(z, "readonly") ){ - p->openMode = SHELL_OPEN_READONLY; - }else if( optionMatch(z, "nofollow") ){ - p->openFlags |= SQLITE_OPEN_NOFOLLOW; -#ifdef SQLITE_ENABLE_DESERIALIZE - }else if( optionMatch(z, "deserialize") ){ - p->openMode = SHELL_OPEN_DESERIALIZE; - }else if( optionMatch(z, "hexdb") ){ - p->openMode = SHELL_OPEN_HEXDB; - }else if( optionMatch(z, "maxsize") && iName+1szMax = integerValue(azArg[++iName]); -#endif /* SQLITE_ENABLE_DESERIALIZE */ - }else if( z[0]=='-' ){ - utf8_printf(stderr, "unknown option: %s\n", z); - rc = 1; - goto meta_command_exit; - } - } - /* If a filename is specified, try to open it first */ - zNewFilename = nArg>iName ? sqlite3_mprintf("%s", azArg[iName]) : 0; - if( zNewFilename || p->openMode==SHELL_OPEN_HEXDB ){ - if( newFlag ) shellDeleteFile(zNewFilename); - p->zDbFilename = zNewFilename; - open_db(p, OPEN_DB_KEEPALIVE); - if( p->db==0 ){ - utf8_printf(stderr, "Error: cannot open '%s'\n", zNewFilename); - sqlite3_free(zNewFilename); - }else{ - p->zFreeOnClose = zNewFilename; - } - } - if( p->db==0 ){ - /* As a fall-back open a TEMP database */ - p->zDbFilename = 0; - open_db(p, 0); - } - }else - - if( (c=='o' - && (strncmp(azArg[0], "output", n)==0||strncmp(azArg[0], "once", n)==0)) - || (c=='e' && n==5 && strcmp(azArg[0],"excel")==0) - ){ - const char *zFile = 0; - int bTxtMode = 0; - int i; - int eMode = 0; - int bBOM = 0; - int bOnce = 0; /* 0: .output, 1: .once, 2: .excel */ - - if( c=='e' ){ - eMode = 'x'; - bOnce = 2; - }else if( strncmp(azArg[0],"once",n)==0 ){ - bOnce = 1; - } - for(i=1; iout, "ERROR: unknown option: \"%s\". Usage:\n", - azArg[i]); - showHelp(p->out, azArg[0]); - rc = 1; - goto meta_command_exit; - } - }else if( zFile==0 ){ - zFile = z; - }else{ - utf8_printf(p->out,"ERROR: extra parameter: \"%s\". Usage:\n", - azArg[i]); - showHelp(p->out, azArg[0]); - rc = 1; - goto meta_command_exit; - } - } - if( zFile==0 ) zFile = "stdout"; - if( bOnce ){ - p->outCount = 2; - }else{ - p->outCount = 0; - } - output_reset(p); -#ifndef SQLITE_NOHAVE_SYSTEM - if( eMode=='e' || eMode=='x' ){ - p->doXdgOpen = 1; - outputModePush(p); - if( eMode=='x' ){ - /* spreadsheet mode. Output as CSV. */ - newTempFile(p, "csv"); - ShellClearFlag(p, SHFLG_Echo); - p->mode = MODE_Csv; - sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, SEP_Comma); - sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_CrLf); - }else{ - /* text editor mode */ - newTempFile(p, "txt"); - bTxtMode = 1; - } - zFile = p->zTempFile; - } -#endif /* SQLITE_NOHAVE_SYSTEM */ - if( zFile[0]=='|' ){ -#ifdef SQLITE_OMIT_POPEN - raw_printf(stderr, "Error: pipes are not supported in this OS\n"); - rc = 1; - p->out = stdout; -#else - p->out = popen(zFile + 1, "w"); - if( p->out==0 ){ - utf8_printf(stderr,"Error: cannot open pipe \"%s\"\n", zFile + 1); - p->out = stdout; - rc = 1; - }else{ - if( bBOM ) fprintf(p->out,"\357\273\277"); - sqlite3_snprintf(sizeof(p->outfile), p->outfile, "%s", zFile); - } -#endif - }else{ - p->out = output_file_open(zFile, bTxtMode); - if( p->out==0 ){ - if( strcmp(zFile,"off")!=0 ){ - utf8_printf(stderr,"Error: cannot write to \"%s\"\n", zFile); - } - p->out = stdout; - rc = 1; - } else { - if( bBOM ) fprintf(p->out,"\357\273\277"); - sqlite3_snprintf(sizeof(p->outfile), p->outfile, "%s", zFile); - } - } - }else - - if( c=='p' && n>=3 && strncmp(azArg[0], "parameter", n)==0 ){ - open_db(p,0); - if( nArg<=1 ) goto parameter_syntax_error; - - /* .parameter clear - ** Clear all bind parameters by dropping the TEMP table that holds them. - */ - if( nArg==2 && strcmp(azArg[1],"clear")==0 ){ - sqlite3_exec(p->db, "DROP TABLE IF EXISTS temp.sqlite_parameters;", - 0, 0, 0); - }else - - /* .parameter list - ** List all bind parameters. - */ - if( nArg==2 && strcmp(azArg[1],"list")==0 ){ - sqlite3_stmt *pStmt = 0; - int rx; - int len = 0; - rx = sqlite3_prepare_v2(p->db, - "SELECT max(length(key)) " - "FROM temp.sqlite_parameters;", -1, &pStmt, 0); - if( rx==SQLITE_OK && sqlite3_step(pStmt)==SQLITE_ROW ){ - len = sqlite3_column_int(pStmt, 0); - if( len>40 ) len = 40; - } - sqlite3_finalize(pStmt); - pStmt = 0; - if( len ){ - rx = sqlite3_prepare_v2(p->db, - "SELECT key, quote(value) " - "FROM temp.sqlite_parameters;", -1, &pStmt, 0); - while( sqlite3_step(pStmt)==SQLITE_ROW ){ - utf8_printf(p->out, "%-*s %s\n", len, sqlite3_column_text(pStmt,0), - sqlite3_column_text(pStmt,1)); - } - sqlite3_finalize(pStmt); - } - }else - - /* .parameter init - ** Make sure the TEMP table used to hold bind parameters exists. - ** Create it if necessary. - */ - if( nArg==2 && strcmp(azArg[1],"init")==0 ){ - bind_table_init(p); - }else - - /* .parameter set NAME VALUE - ** Set or reset a bind parameter. NAME should be the full parameter - ** name exactly as it appears in the query. (ex: $abc, @def). The - ** VALUE can be in either SQL literal notation, or if not it will be - ** understood to be a text string. - */ - if( nArg==4 && strcmp(azArg[1],"set")==0 ){ - int rx; - char *zSql; - sqlite3_stmt *pStmt; - const char *zKey = azArg[2]; - const char *zValue = azArg[3]; - bind_table_init(p); - zSql = sqlite3_mprintf( - "REPLACE INTO temp.sqlite_parameters(key,value)" - "VALUES(%Q,%s);", zKey, zValue); - if( zSql==0 ) shell_out_of_memory(); - pStmt = 0; - rx = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0); - sqlite3_free(zSql); - if( rx!=SQLITE_OK ){ - sqlite3_finalize(pStmt); - pStmt = 0; - zSql = sqlite3_mprintf( - "REPLACE INTO temp.sqlite_parameters(key,value)" - "VALUES(%Q,%Q);", zKey, zValue); - if( zSql==0 ) shell_out_of_memory(); - rx = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0); - sqlite3_free(zSql); - if( rx!=SQLITE_OK ){ - utf8_printf(p->out, "Error: %s\n", sqlite3_errmsg(p->db)); - sqlite3_finalize(pStmt); - pStmt = 0; - rc = 1; - } - } - sqlite3_step(pStmt); - sqlite3_finalize(pStmt); - }else - - /* .parameter unset NAME - ** Remove the NAME binding from the parameter binding table, if it - ** exists. - */ - if( nArg==3 && strcmp(azArg[1],"unset")==0 ){ - char *zSql = sqlite3_mprintf( - "DELETE FROM temp.sqlite_parameters WHERE key=%Q", azArg[2]); - if( zSql==0 ) shell_out_of_memory(); - sqlite3_exec(p->db, zSql, 0, 0, 0); - sqlite3_free(zSql); - }else - /* If no command name matches, show a syntax error */ - parameter_syntax_error: - showHelp(p->out, "parameter"); - }else - - if( c=='p' && n>=3 && strncmp(azArg[0], "print", n)==0 ){ - int i; - for(i=1; i1 ) raw_printf(p->out, " "); - utf8_printf(p->out, "%s", azArg[i]); - } - raw_printf(p->out, "\n"); - }else - -#ifndef SQLITE_OMIT_PROGRESS_CALLBACK - if( c=='p' && n>=3 && strncmp(azArg[0], "progress", n)==0 ){ - int i; - int nn = 0; - p->flgProgress = 0; - p->mxProgress = 0; - p->nProgress = 0; - for(i=1; iflgProgress |= SHELL_PROGRESS_QUIET; - continue; - } - if( strcmp(z,"reset")==0 ){ - p->flgProgress |= SHELL_PROGRESS_RESET; - continue; - } - if( strcmp(z,"once")==0 ){ - p->flgProgress |= SHELL_PROGRESS_ONCE; - continue; - } - if( strcmp(z,"limit")==0 ){ - if( i+1>=nArg ){ - utf8_printf(stderr, "Error: missing argument on --limit\n"); - rc = 1; - goto meta_command_exit; - }else{ - p->mxProgress = (int)integerValue(azArg[++i]); - } - continue; - } - utf8_printf(stderr, "Error: unknown option: \"%s\"\n", azArg[i]); - rc = 1; - goto meta_command_exit; - }else{ - nn = (int)integerValue(z); - } - } - open_db(p, 0); - sqlite3_progress_handler(p->db, nn, progress_handler, p); - }else -#endif /* SQLITE_OMIT_PROGRESS_CALLBACK */ - - if( c=='p' && strncmp(azArg[0], "prompt", n)==0 ){ - if( nArg >= 2) { - strncpy(mainPrompt,azArg[1],(int)ArraySize(mainPrompt)-1); - } - if( nArg >= 3) { - strncpy(continuePrompt,azArg[2],(int)ArraySize(continuePrompt)-1); - } - }else - - if( c=='q' && strncmp(azArg[0], "quit", n)==0 ){ - rc = 2; - }else - - if( c=='r' && n>=3 && strncmp(azArg[0], "read", n)==0 ){ - FILE *inSaved = p->in; - int savedLineno = p->lineno; - if( nArg!=2 ){ - raw_printf(stderr, "Usage: .read FILE\n"); - rc = 1; - goto meta_command_exit; - } - if( notNormalFile(azArg[1]) - || (p->in = fopen(azArg[1], "rb"))==0 - ){ - utf8_printf(stderr,"Error: cannot open \"%s\"\n", azArg[1]); - rc = 1; - }else{ - rc = process_input(p); - fclose(p->in); - } - p->in = inSaved; - p->lineno = savedLineno; - }else - - if( c=='r' && n>=3 && strncmp(azArg[0], "restore", n)==0 ){ - const char *zSrcFile; - const char *zDb; - sqlite3 *pSrc; - sqlite3_backup *pBackup; - int nTimeout = 0; - - if( nArg==2 ){ - zSrcFile = azArg[1]; - zDb = "main"; - }else if( nArg==3 ){ - zSrcFile = azArg[2]; - zDb = azArg[1]; - }else{ - raw_printf(stderr, "Usage: .restore ?DB? FILE\n"); - rc = 1; - goto meta_command_exit; - } - rc = sqlite3_open(zSrcFile, &pSrc); - if( rc!=SQLITE_OK ){ - utf8_printf(stderr, "Error: cannot open \"%s\"\n", zSrcFile); - close_db(pSrc); - return 1; - } - open_db(p, 0); - pBackup = sqlite3_backup_init(p->db, zDb, pSrc, "main"); - if( pBackup==0 ){ - utf8_printf(stderr, "Error: %s\n", sqlite3_errmsg(p->db)); - close_db(pSrc); - return 1; - } - while( (rc = sqlite3_backup_step(pBackup,100))==SQLITE_OK - || rc==SQLITE_BUSY ){ - if( rc==SQLITE_BUSY ){ - if( nTimeout++ >= 3 ) break; - sqlite3_sleep(100); - } - } - sqlite3_backup_finish(pBackup); - if( rc==SQLITE_DONE ){ - rc = 0; - }else if( rc==SQLITE_BUSY || rc==SQLITE_LOCKED ){ - raw_printf(stderr, "Error: source database is busy\n"); - rc = 1; - }else{ - utf8_printf(stderr, "Error: %s\n", sqlite3_errmsg(p->db)); - rc = 1; - } - close_db(pSrc); - }else - - if( c=='s' && strncmp(azArg[0], "scanstats", n)==0 ){ - if( nArg==2 ){ - p->scanstatsOn = (u8)booleanValue(azArg[1]); -#ifndef SQLITE_ENABLE_STMT_SCANSTATUS - raw_printf(stderr, "Warning: .scanstats not available in this build.\n"); -#endif - }else{ - raw_printf(stderr, "Usage: .scanstats on|off\n"); - rc = 1; - } - }else - - if( c=='s' && strncmp(azArg[0], "schema", n)==0 ){ - ShellText sSelect; - ShellState data; - char *zErrMsg = 0; - const char *zDiv = "("; - const char *zName = 0; - int iSchema = 0; - int bDebug = 0; - int ii; - - open_db(p, 0); - memcpy(&data, p, sizeof(data)); - data.showHeader = 0; - data.cMode = data.mode = MODE_Semi; - initText(&sSelect); - for(ii=1; iidb, "SELECT name FROM pragma_database_list", - -1, &pStmt, 0); - if( rc ){ - utf8_printf(stderr, "Error: %s\n", sqlite3_errmsg(p->db)); - sqlite3_finalize(pStmt); - rc = 1; - goto meta_command_exit; - } - appendText(&sSelect, "SELECT sql FROM", 0); - iSchema = 0; - while( sqlite3_step(pStmt)==SQLITE_ROW ){ - const char *zDb = (const char*)sqlite3_column_text(pStmt, 0); - char zScNum[30]; - sqlite3_snprintf(sizeof(zScNum), zScNum, "%d", ++iSchema); - appendText(&sSelect, zDiv, 0); - zDiv = " UNION ALL "; - appendText(&sSelect, "SELECT shell_add_schema(sql,", 0); - if( sqlite3_stricmp(zDb, "main")!=0 ){ - appendText(&sSelect, zDb, '\''); - }else{ - appendText(&sSelect, "NULL", 0); - } - appendText(&sSelect, ",name) AS sql, type, tbl_name, name, rowid,", 0); - appendText(&sSelect, zScNum, 0); - appendText(&sSelect, " AS snum, ", 0); - appendText(&sSelect, zDb, '\''); - appendText(&sSelect, " AS sname FROM ", 0); - appendText(&sSelect, zDb, quoteChar(zDb)); - appendText(&sSelect, ".sqlite_schema", 0); - } - sqlite3_finalize(pStmt); -#ifndef SQLITE_OMIT_INTROSPECTION_PRAGMAS - if( zName ){ - appendText(&sSelect, - " UNION ALL SELECT shell_module_schema(name)," - " 'table', name, name, name, 9e+99, 'main' FROM pragma_module_list", - 0); - } -#endif - appendText(&sSelect, ") WHERE ", 0); - if( zName ){ - char *zQarg = sqlite3_mprintf("%Q", zName); - int bGlob = strchr(zName, '*') != 0 || strchr(zName, '?') != 0 || - strchr(zName, '[') != 0; - if( strchr(zName, '.') ){ - appendText(&sSelect, "lower(printf('%s.%s',sname,tbl_name))", 0); - }else{ - appendText(&sSelect, "lower(tbl_name)", 0); - } - appendText(&sSelect, bGlob ? " GLOB " : " LIKE ", 0); - appendText(&sSelect, zQarg, 0); - if( !bGlob ){ - appendText(&sSelect, " ESCAPE '\\' ", 0); - } - appendText(&sSelect, " AND ", 0); - sqlite3_free(zQarg); - } - appendText(&sSelect, "type!='meta' AND sql IS NOT NULL" - " ORDER BY snum, rowid", 0); - if( bDebug ){ - utf8_printf(p->out, "SQL: %s;\n", sSelect.z); - }else{ - rc = sqlite3_exec(p->db, sSelect.z, callback, &data, &zErrMsg); - } - freeText(&sSelect); - } - if( zErrMsg ){ - utf8_printf(stderr,"Error: %s\n", zErrMsg); - sqlite3_free(zErrMsg); - rc = 1; - }else if( rc != SQLITE_OK ){ - raw_printf(stderr,"Error: querying schema information\n"); - rc = 1; - }else{ - rc = 0; - } - }else - -#if defined(SQLITE_DEBUG) && defined(SQLITE_ENABLE_SELECTTRACE) - if( c=='s' && n==11 && strncmp(azArg[0], "selecttrace", n)==0 ){ - sqlite3_unsupported_selecttrace = nArg>=2 ? (int)integerValue(azArg[1]) : 0xffff; - }else -#endif - -#if defined(SQLITE_ENABLE_SESSION) - if( c=='s' && strncmp(azArg[0],"session",n)==0 && n>=3 ){ - OpenSession *pSession = &p->aSession[0]; - char **azCmd = &azArg[1]; - int iSes = 0; - int nCmd = nArg - 1; - int i; - if( nArg<=1 ) goto session_syntax_error; - open_db(p, 0); - if( nArg>=3 ){ - for(iSes=0; iSesnSession; iSes++){ - if( strcmp(p->aSession[iSes].zName, azArg[1])==0 ) break; - } - if( iSesnSession ){ - pSession = &p->aSession[iSes]; - azCmd++; - nCmd--; - }else{ - pSession = &p->aSession[0]; - iSes = 0; - } - } - - /* .session attach TABLE - ** Invoke the sqlite3session_attach() interface to attach a particular - ** table so that it is never filtered. - */ - if( strcmp(azCmd[0],"attach")==0 ){ - if( nCmd!=2 ) goto session_syntax_error; - if( pSession->p==0 ){ - session_not_open: - raw_printf(stderr, "ERROR: No sessions are open\n"); - }else{ - rc = sqlite3session_attach(pSession->p, azCmd[1]); - if( rc ){ - raw_printf(stderr, "ERROR: sqlite3session_attach() returns %d\n", rc); - rc = 0; - } - } - }else - - /* .session changeset FILE - ** .session patchset FILE - ** Write a changeset or patchset into a file. The file is overwritten. - */ - if( strcmp(azCmd[0],"changeset")==0 || strcmp(azCmd[0],"patchset")==0 ){ - FILE *out = 0; - if( nCmd!=2 ) goto session_syntax_error; - if( pSession->p==0 ) goto session_not_open; - out = fopen(azCmd[1], "wb"); - if( out==0 ){ - utf8_printf(stderr, "ERROR: cannot open \"%s\" for writing\n", - azCmd[1]); - }else{ - int szChng; - void *pChng; - if( azCmd[0][0]=='c' ){ - rc = sqlite3session_changeset(pSession->p, &szChng, &pChng); - }else{ - rc = sqlite3session_patchset(pSession->p, &szChng, &pChng); - } - if( rc ){ - printf("Error: error code %d\n", rc); - rc = 0; - } - if( pChng - && fwrite(pChng, szChng, 1, out)!=1 ){ - raw_printf(stderr, "ERROR: Failed to write entire %d-byte output\n", - szChng); - } - sqlite3_free(pChng); - fclose(out); - } - }else - - /* .session close - ** Close the identified session - */ - if( strcmp(azCmd[0], "close")==0 ){ - if( nCmd!=1 ) goto session_syntax_error; - if( p->nSession ){ - session_close(pSession); - p->aSession[iSes] = p->aSession[--p->nSession]; - } - }else - - /* .session enable ?BOOLEAN? - ** Query or set the enable flag - */ - if( strcmp(azCmd[0], "enable")==0 ){ - int ii; - if( nCmd>2 ) goto session_syntax_error; - ii = nCmd==1 ? -1 : booleanValue(azCmd[1]); - if( p->nSession ){ - ii = sqlite3session_enable(pSession->p, ii); - utf8_printf(p->out, "session %s enable flag = %d\n", - pSession->zName, ii); - } - }else - - /* .session filter GLOB .... - ** Set a list of GLOB patterns of table names to be excluded. - */ - if( strcmp(azCmd[0], "filter")==0 ){ - int ii, nByte; - if( nCmd<2 ) goto session_syntax_error; - if( p->nSession ){ - for(ii=0; iinFilter; ii++){ - sqlite3_free(pSession->azFilter[ii]); - } - sqlite3_free(pSession->azFilter); - nByte = sizeof(pSession->azFilter[0])*(nCmd-1); - pSession->azFilter = sqlite3_malloc( nByte ); - if( pSession->azFilter==0 ){ - raw_printf(stderr, "Error: out or memory\n"); - exit(1); - } - for(ii=1; iiazFilter[ii-1] = sqlite3_mprintf("%s", azCmd[ii]); - } - pSession->nFilter = ii-1; - } - }else - - /* .session indirect ?BOOLEAN? - ** Query or set the indirect flag - */ - if( strcmp(azCmd[0], "indirect")==0 ){ - int ii; - if( nCmd>2 ) goto session_syntax_error; - ii = nCmd==1 ? -1 : booleanValue(azCmd[1]); - if( p->nSession ){ - ii = sqlite3session_indirect(pSession->p, ii); - utf8_printf(p->out, "session %s indirect flag = %d\n", - pSession->zName, ii); - } - }else - - /* .session isempty - ** Determine if the session is empty - */ - if( strcmp(azCmd[0], "isempty")==0 ){ - int ii; - if( nCmd!=1 ) goto session_syntax_error; - if( p->nSession ){ - ii = sqlite3session_isempty(pSession->p); - utf8_printf(p->out, "session %s isempty flag = %d\n", - pSession->zName, ii); - } - }else - - /* .session list - ** List all currently open sessions - */ - if( strcmp(azCmd[0],"list")==0 ){ - for(i=0; inSession; i++){ - utf8_printf(p->out, "%d %s\n", i, p->aSession[i].zName); - } - }else - - /* .session open DB NAME - ** Open a new session called NAME on the attached database DB. - ** DB is normally "main". - */ - if( strcmp(azCmd[0],"open")==0 ){ - char *zName; - if( nCmd!=3 ) goto session_syntax_error; - zName = azCmd[2]; - if( zName[0]==0 ) goto session_syntax_error; - for(i=0; inSession; i++){ - if( strcmp(p->aSession[i].zName,zName)==0 ){ - utf8_printf(stderr, "Session \"%s\" already exists\n", zName); - goto meta_command_exit; - } - } - if( p->nSession>=ArraySize(p->aSession) ){ - raw_printf(stderr, "Maximum of %d sessions\n", ArraySize(p->aSession)); - goto meta_command_exit; - } - pSession = &p->aSession[p->nSession]; - rc = sqlite3session_create(p->db, azCmd[1], &pSession->p); - if( rc ){ - raw_printf(stderr, "Cannot open session: error code=%d\n", rc); - rc = 0; - goto meta_command_exit; - } - pSession->nFilter = 0; - sqlite3session_table_filter(pSession->p, session_filter, pSession); - p->nSession++; - pSession->zName = sqlite3_mprintf("%s", zName); - }else - /* If no command name matches, show a syntax error */ - session_syntax_error: - showHelp(p->out, "session"); - }else -#endif - -#ifdef SQLITE_DEBUG - /* Undocumented commands for internal testing. Subject to change - ** without notice. */ - if( c=='s' && n>=10 && strncmp(azArg[0], "selftest-", 9)==0 ){ - if( strncmp(azArg[0]+9, "boolean", n-9)==0 ){ - int i, v; - for(i=1; iout, "%s: %d 0x%x\n", azArg[i], v, v); - } - } - if( strncmp(azArg[0]+9, "integer", n-9)==0 ){ - int i; sqlite3_int64 v; - for(i=1; iout, "%s", zBuf); - } - } - }else -#endif - - if( c=='s' && n>=4 && strncmp(azArg[0],"selftest",n)==0 ){ - int bIsInit = 0; /* True to initialize the SELFTEST table */ - int bVerbose = 0; /* Verbose output */ - int bSelftestExists; /* True if SELFTEST already exists */ - int i, k; /* Loop counters */ - int nTest = 0; /* Number of tests runs */ - int nErr = 0; /* Number of errors seen */ - ShellText str; /* Answer for a query */ - sqlite3_stmt *pStmt = 0; /* Query against the SELFTEST table */ - - open_db(p,0); - for(i=1; idb,"main","selftest",0,0,0,0,0,0) - != SQLITE_OK ){ - bSelftestExists = 0; - }else{ - bSelftestExists = 1; - } - if( bIsInit ){ - createSelftestTable(p); - bSelftestExists = 1; - } - initText(&str); - appendText(&str, "x", 0); - for(k=bSelftestExists; k>=0; k--){ - if( k==1 ){ - rc = sqlite3_prepare_v2(p->db, - "SELECT tno,op,cmd,ans FROM selftest ORDER BY tno", - -1, &pStmt, 0); - }else{ - rc = sqlite3_prepare_v2(p->db, - "VALUES(0,'memo','Missing SELFTEST table - default checks only','')," - " (1,'run','PRAGMA integrity_check','ok')", - -1, &pStmt, 0); - } - if( rc ){ - raw_printf(stderr, "Error querying the selftest table\n"); - rc = 1; - sqlite3_finalize(pStmt); - goto meta_command_exit; - } - for(i=1; sqlite3_step(pStmt)==SQLITE_ROW; i++){ - int tno = sqlite3_column_int(pStmt, 0); - const char *zOp = (const char*)sqlite3_column_text(pStmt, 1); - const char *zSql = (const char*)sqlite3_column_text(pStmt, 2); - const char *zAns = (const char*)sqlite3_column_text(pStmt, 3); - - k = 0; - if( bVerbose>0 ){ - char *zQuote = sqlite3_mprintf("%q", zSql); - printf("%d: %s %s\n", tno, zOp, zSql); - sqlite3_free(zQuote); - } - if( strcmp(zOp,"memo")==0 ){ - utf8_printf(p->out, "%s\n", zSql); - }else - if( strcmp(zOp,"run")==0 ){ - char *zErrMsg = 0; - str.n = 0; - str.z[0] = 0; - rc = sqlite3_exec(p->db, zSql, captureOutputCallback, &str, &zErrMsg); - nTest++; - if( bVerbose ){ - utf8_printf(p->out, "Result: %s\n", str.z); - } - if( rc || zErrMsg ){ - nErr++; - rc = 1; - utf8_printf(p->out, "%d: error-code-%d: %s\n", tno, rc, zErrMsg); - sqlite3_free(zErrMsg); - }else if( strcmp(zAns,str.z)!=0 ){ - nErr++; - rc = 1; - utf8_printf(p->out, "%d: Expected: [%s]\n", tno, zAns); - utf8_printf(p->out, "%d: Got: [%s]\n", tno, str.z); - } - }else - { - utf8_printf(stderr, - "Unknown operation \"%s\" on selftest line %d\n", zOp, tno); - rc = 1; - break; - } - } /* End loop over rows of content from SELFTEST */ - sqlite3_finalize(pStmt); - } /* End loop over k */ - freeText(&str); - utf8_printf(p->out, "%d errors out of %d tests\n", nErr, nTest); - }else - - if( c=='s' && strncmp(azArg[0], "separator", n)==0 ){ - if( nArg<2 || nArg>3 ){ - raw_printf(stderr, "Usage: .separator COL ?ROW?\n"); - rc = 1; - } - if( nArg>=2 ){ - sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, - "%.*s", (int)ArraySize(p->colSeparator)-1, azArg[1]); - } - if( nArg>=3 ){ - sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, - "%.*s", (int)ArraySize(p->rowSeparator)-1, azArg[2]); - } - }else - - if( c=='s' && n>=4 && strncmp(azArg[0],"sha3sum",n)==0 ){ - const char *zLike = 0; /* Which table to checksum. 0 means everything */ - int i; /* Loop counter */ - int bSchema = 0; /* Also hash the schema */ - int bSeparate = 0; /* Hash each table separately */ - int iSize = 224; /* Hash algorithm to use */ - int bDebug = 0; /* Only show the query that would have run */ - sqlite3_stmt *pStmt; /* For querying tables names */ - char *zSql; /* SQL to be run */ - char *zSep; /* Separator */ - ShellText sSql; /* Complete SQL for the query to run the hash */ - ShellText sQuery; /* Set of queries used to read all content */ - open_db(p, 0); - for(i=1; iout, azArg[0]); - rc = 1; - goto meta_command_exit; - } - }else if( zLike ){ - raw_printf(stderr, "Usage: .sha3sum ?OPTIONS? ?LIKE-PATTERN?\n"); - rc = 1; - goto meta_command_exit; - }else{ - zLike = z; - bSeparate = 1; - if( sqlite3_strlike("sqlite\\_%", zLike, '\\')==0 ) bSchema = 1; - } - } - if( bSchema ){ - zSql = "SELECT lower(name) FROM sqlite_schema" - " WHERE type='table' AND coalesce(rootpage,0)>1" - " UNION ALL SELECT 'sqlite_schema'" - " ORDER BY 1 collate nocase"; - }else{ - zSql = "SELECT lower(name) FROM sqlite_schema" - " WHERE type='table' AND coalesce(rootpage,0)>1" - " AND name NOT LIKE 'sqlite_%'" - " ORDER BY 1 collate nocase"; - } - sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0); - initText(&sQuery); - initText(&sSql); - appendText(&sSql, "WITH [sha3sum$query](a,b) AS(",0); - zSep = "VALUES("; - while( SQLITE_ROW==sqlite3_step(pStmt) ){ - const char *zTab = (const char*)sqlite3_column_text(pStmt,0); - if( zLike && sqlite3_strlike(zLike, zTab, 0)!=0 ) continue; - if( strncmp(zTab, "sqlite_",7)!=0 ){ - appendText(&sQuery,"SELECT * FROM ", 0); - appendText(&sQuery,zTab,'"'); - appendText(&sQuery," NOT INDEXED;", 0); - }else if( strcmp(zTab, "sqlite_schema")==0 ){ - appendText(&sQuery,"SELECT type,name,tbl_name,sql FROM sqlite_schema" - " ORDER BY name;", 0); - }else if( strcmp(zTab, "sqlite_sequence")==0 ){ - appendText(&sQuery,"SELECT name,seq FROM sqlite_sequence" - " ORDER BY name;", 0); - }else if( strcmp(zTab, "sqlite_stat1")==0 ){ - appendText(&sQuery,"SELECT tbl,idx,stat FROM sqlite_stat1" - " ORDER BY tbl,idx;", 0); - }else if( strcmp(zTab, "sqlite_stat4")==0 ){ - appendText(&sQuery, "SELECT * FROM ", 0); - appendText(&sQuery, zTab, 0); - appendText(&sQuery, " ORDER BY tbl, idx, rowid;\n", 0); - } - appendText(&sSql, zSep, 0); - appendText(&sSql, sQuery.z, '\''); - sQuery.n = 0; - appendText(&sSql, ",", 0); - appendText(&sSql, zTab, '\''); - zSep = "),("; - } - sqlite3_finalize(pStmt); - if( bSeparate ){ - zSql = sqlite3_mprintf( - "%s))" - " SELECT lower(hex(sha3_query(a,%d))) AS hash, b AS label" - " FROM [sha3sum$query]", - sSql.z, iSize); - }else{ - zSql = sqlite3_mprintf( - "%s))" - " SELECT lower(hex(sha3_query(group_concat(a,''),%d))) AS hash" - " FROM [sha3sum$query]", - sSql.z, iSize); - } - freeText(&sQuery); - freeText(&sSql); - if( bDebug ){ - utf8_printf(p->out, "%s\n", zSql); - }else{ - shell_exec(p, zSql, 0); - } - sqlite3_free(zSql); - }else - -#ifndef SQLITE_NOHAVE_SYSTEM - if( c=='s' - && (strncmp(azArg[0], "shell", n)==0 || strncmp(azArg[0],"system",n)==0) - ){ - char *zCmd; - int i, x; - if( nArg<2 ){ - raw_printf(stderr, "Usage: .system COMMAND\n"); - rc = 1; - goto meta_command_exit; - } - zCmd = sqlite3_mprintf(strchr(azArg[1],' ')==0?"%s":"\"%s\"", azArg[1]); - for(i=2; iout, "%12.12s: %s\n","echo", - azBool[ShellHasFlag(p, SHFLG_Echo)]); - utf8_printf(p->out, "%12.12s: %s\n","eqp", azBool[p->autoEQP&3]); - utf8_printf(p->out, "%12.12s: %s\n","explain", - p->mode==MODE_Explain ? "on" : p->autoExplain ? "auto" : "off"); - utf8_printf(p->out,"%12.12s: %s\n","headers", azBool[p->showHeader!=0]); - utf8_printf(p->out, "%12.12s: %s\n","mode", modeDescr[p->mode]); - utf8_printf(p->out, "%12.12s: ", "nullvalue"); - output_c_string(p->out, p->nullValue); - raw_printf(p->out, "\n"); - utf8_printf(p->out,"%12.12s: %s\n","output", - strlen30(p->outfile) ? p->outfile : "stdout"); - utf8_printf(p->out,"%12.12s: ", "colseparator"); - output_c_string(p->out, p->colSeparator); - raw_printf(p->out, "\n"); - utf8_printf(p->out,"%12.12s: ", "rowseparator"); - output_c_string(p->out, p->rowSeparator); - raw_printf(p->out, "\n"); - utf8_printf(p->out, "%12.12s: %s\n","stats", azBool[p->statsOn!=0]); - utf8_printf(p->out, "%12.12s: ", "width"); - for (i=0;inWidth;i++) { - raw_printf(p->out, "%d ", p->colWidth[i]); - } - raw_printf(p->out, "\n"); - utf8_printf(p->out, "%12.12s: %s\n", "filename", - p->zDbFilename ? p->zDbFilename : ""); - }else - - if( c=='s' && strncmp(azArg[0], "stats", n)==0 ){ - if( nArg==2 ){ - p->statsOn = (u8)booleanValue(azArg[1]); - }else if( nArg==1 ){ - display_stats(p->db, p, 0); - }else{ - raw_printf(stderr, "Usage: .stats ?on|off?\n"); - rc = 1; - } - }else - - if( (c=='t' && n>1 && strncmp(azArg[0], "tables", n)==0) - || (c=='i' && (strncmp(azArg[0], "indices", n)==0 - || strncmp(azArg[0], "indexes", n)==0) ) - ){ - sqlite3_stmt *pStmt; - char **azResult; - int nRow, nAlloc; - int ii; - ShellText s; - initText(&s); - open_db(p, 0); - rc = sqlite3_prepare_v2(p->db, "PRAGMA database_list", -1, &pStmt, 0); - if( rc ){ - sqlite3_finalize(pStmt); - return shellDatabaseError(p->db); - } - - if( nArg>2 && c=='i' ){ - /* It is an historical accident that the .indexes command shows an error - ** when called with the wrong number of arguments whereas the .tables - ** command does not. */ - raw_printf(stderr, "Usage: .indexes ?LIKE-PATTERN?\n"); - rc = 1; - sqlite3_finalize(pStmt); - goto meta_command_exit; - } - for(ii=0; sqlite3_step(pStmt)==SQLITE_ROW; ii++){ - const char *zDbName = (const char*)sqlite3_column_text(pStmt, 1); - if( zDbName==0 ) continue; - if( s.z && s.z[0] ) appendText(&s, " UNION ALL ", 0); - if( sqlite3_stricmp(zDbName, "main")==0 ){ - appendText(&s, "SELECT name FROM ", 0); - }else{ - appendText(&s, "SELECT ", 0); - appendText(&s, zDbName, '\''); - appendText(&s, "||'.'||name FROM ", 0); - } - appendText(&s, zDbName, '"'); - appendText(&s, ".sqlite_schema ", 0); - if( c=='t' ){ - appendText(&s," WHERE type IN ('table','view')" - " AND name NOT LIKE 'sqlite_%'" - " AND name LIKE ?1", 0); - }else{ - appendText(&s," WHERE type='index'" - " AND tbl_name LIKE ?1", 0); - } - } - rc = sqlite3_finalize(pStmt); - appendText(&s, " ORDER BY 1", 0); - rc = sqlite3_prepare_v2(p->db, s.z, -1, &pStmt, 0); - freeText(&s); - if( rc ) return shellDatabaseError(p->db); - - /* Run the SQL statement prepared by the above block. Store the results - ** as an array of nul-terminated strings in azResult[]. */ - nRow = nAlloc = 0; - azResult = 0; - if( nArg>1 ){ - sqlite3_bind_text(pStmt, 1, azArg[1], -1, SQLITE_TRANSIENT); - }else{ - sqlite3_bind_text(pStmt, 1, "%", -1, SQLITE_STATIC); - } - while( sqlite3_step(pStmt)==SQLITE_ROW ){ - if( nRow>=nAlloc ){ - char **azNew; - int n2 = nAlloc*2 + 10; - azNew = sqlite3_realloc64(azResult, sizeof(azResult[0])*n2); - if( azNew==0 ) shell_out_of_memory(); - nAlloc = n2; - azResult = azNew; - } - azResult[nRow] = sqlite3_mprintf("%s", sqlite3_column_text(pStmt, 0)); - if( 0==azResult[nRow] ) shell_out_of_memory(); - nRow++; - } - if( sqlite3_finalize(pStmt)!=SQLITE_OK ){ - rc = shellDatabaseError(p->db); - } - - /* Pretty-print the contents of array azResult[] to the output */ - if( rc==0 && nRow>0 ){ - int len, maxlen = 0; - int i, j; - int nPrintCol, nPrintRow; - for(i=0; imaxlen ) maxlen = len; - } - nPrintCol = 80/(maxlen+2); - if( nPrintCol<1 ) nPrintCol = 1; - nPrintRow = (nRow + nPrintCol - 1)/nPrintCol; - for(i=0; iout, "%s%-*s", zSp, maxlen, - azResult[j] ? azResult[j]:""); - } - raw_printf(p->out, "\n"); - } - } - - for(ii=0; iiout = output_file_open("testcase-out.txt", 0); - if( p->out==0 ){ - raw_printf(stderr, "Error: cannot open 'testcase-out.txt'\n"); - } - if( nArg>=2 ){ - sqlite3_snprintf(sizeof(p->zTestcase), p->zTestcase, "%s", azArg[1]); - }else{ - sqlite3_snprintf(sizeof(p->zTestcase), p->zTestcase, "?"); - } - }else - -#ifndef SQLITE_UNTESTABLE - if( c=='t' && n>=8 && strncmp(azArg[0], "testctrl", n)==0 ){ - static const struct { - const char *zCtrlName; /* Name of a test-control option */ - int ctrlCode; /* Integer code for that option */ - const char *zUsage; /* Usage notes */ - } aCtrl[] = { - { "always", SQLITE_TESTCTRL_ALWAYS, "BOOLEAN" }, - { "assert", SQLITE_TESTCTRL_ASSERT, "BOOLEAN" }, - /*{ "benign_malloc_hooks",SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS, "" },*/ - /*{ "bitvec_test", SQLITE_TESTCTRL_BITVEC_TEST, "" },*/ - { "byteorder", SQLITE_TESTCTRL_BYTEORDER, "" }, - { "extra_schema_checks",SQLITE_TESTCTRL_EXTRA_SCHEMA_CHECKS,"BOOLEAN" }, - /*{ "fault_install", SQLITE_TESTCTRL_FAULT_INSTALL, "" },*/ - { "imposter", SQLITE_TESTCTRL_IMPOSTER, "SCHEMA ON/OFF ROOTPAGE"}, - { "internal_functions", SQLITE_TESTCTRL_INTERNAL_FUNCTIONS, "" }, - { "localtime_fault", SQLITE_TESTCTRL_LOCALTIME_FAULT,"BOOLEAN" }, - { "never_corrupt", SQLITE_TESTCTRL_NEVER_CORRUPT, "BOOLEAN" }, - { "optimizations", SQLITE_TESTCTRL_OPTIMIZATIONS, "DISABLE-MASK" }, -#ifdef YYCOVERAGE - { "parser_coverage", SQLITE_TESTCTRL_PARSER_COVERAGE, "" }, -#endif - { "pending_byte", SQLITE_TESTCTRL_PENDING_BYTE, "OFFSET " }, - { "prng_restore", SQLITE_TESTCTRL_PRNG_RESTORE, "" }, - { "prng_save", SQLITE_TESTCTRL_PRNG_SAVE, "" }, - { "prng_seed", SQLITE_TESTCTRL_PRNG_SEED, "SEED ?db?" }, - }; - int testctrl = -1; - int iCtrl = -1; - int rc2 = 0; /* 0: usage. 1: %d 2: %x 3: no-output */ - int isOk = 0; - int i, n2; - const char *zCmd = 0; - - open_db(p, 0); - zCmd = nArg>=2 ? azArg[1] : "help"; - - /* The argument can optionally begin with "-" or "--" */ - if( zCmd[0]=='-' && zCmd[1] ){ - zCmd++; - if( zCmd[0]=='-' && zCmd[1] ) zCmd++; - } - - /* --help lists all test-controls */ - if( strcmp(zCmd,"help")==0 ){ - utf8_printf(p->out, "Available test-controls:\n"); - for(i=0; iout, " .testctrl %s %s\n", - aCtrl[i].zCtrlName, aCtrl[i].zUsage); - } - rc = 1; - goto meta_command_exit; - } - - /* convert testctrl text option to value. allow any unique prefix - ** of the option name, or a numerical value. */ - n2 = strlen30(zCmd); - for(i=0; idb, opt); - isOk = 3; - } - break; - - /* sqlite3_test_control(int) */ - case SQLITE_TESTCTRL_PRNG_SAVE: - case SQLITE_TESTCTRL_PRNG_RESTORE: - case SQLITE_TESTCTRL_PRNG_RESET: - case SQLITE_TESTCTRL_BYTEORDER: - if( nArg==2 ){ - rc2 = sqlite3_test_control(testctrl); - isOk = testctrl==SQLITE_TESTCTRL_BYTEORDER ? 1 : 3; - } - break; - - /* sqlite3_test_control(int, uint) */ - case SQLITE_TESTCTRL_PENDING_BYTE: - if( nArg==3 ){ - unsigned int opt = (unsigned int)integerValue(azArg[2]); - rc2 = sqlite3_test_control(testctrl, opt); - isOk = 3; - } - break; - - /* sqlite3_test_control(int, int, sqlite3*) */ - case SQLITE_TESTCTRL_PRNG_SEED: - if( nArg==3 || nArg==4 ){ - int ii = (int)integerValue(azArg[2]); - sqlite3 *db; - if( ii==0 && strcmp(azArg[2],"random")==0 ){ - sqlite3_randomness(sizeof(ii),&ii); - printf("-- random seed: %d\n", ii); - } - if( nArg==3 ){ - db = 0; - }else{ - db = p->db; - /* Make sure the schema has been loaded */ - sqlite3_table_column_metadata(db, 0, "x", 0, 0, 0, 0, 0, 0); - } - rc2 = sqlite3_test_control(testctrl, ii, db); - isOk = 3; - } - break; - - /* sqlite3_test_control(int, int) */ - case SQLITE_TESTCTRL_ASSERT: - case SQLITE_TESTCTRL_ALWAYS: - if( nArg==3 ){ - int opt = booleanValue(azArg[2]); - rc2 = sqlite3_test_control(testctrl, opt); - isOk = 1; - } - break; - - /* sqlite3_test_control(int, int) */ - case SQLITE_TESTCTRL_LOCALTIME_FAULT: - case SQLITE_TESTCTRL_NEVER_CORRUPT: - if( nArg==3 ){ - int opt = booleanValue(azArg[2]); - rc2 = sqlite3_test_control(testctrl, opt); - isOk = 3; - } - break; - - /* sqlite3_test_control(sqlite3*) */ - case SQLITE_TESTCTRL_INTERNAL_FUNCTIONS: - rc2 = sqlite3_test_control(testctrl, p->db); - isOk = 3; - break; - - case SQLITE_TESTCTRL_IMPOSTER: - if( nArg==5 ){ - rc2 = sqlite3_test_control(testctrl, p->db, - azArg[2], - integerValue(azArg[3]), - integerValue(azArg[4])); - isOk = 3; - } - break; - -#ifdef YYCOVERAGE - case SQLITE_TESTCTRL_PARSER_COVERAGE: - if( nArg==2 ){ - sqlite3_test_control(testctrl, p->out); - isOk = 3; - } -#endif - } - } - if( isOk==0 && iCtrl>=0 ){ - utf8_printf(p->out, "Usage: .testctrl %s %s\n", zCmd,aCtrl[iCtrl].zUsage); - rc = 1; - }else if( isOk==1 ){ - raw_printf(p->out, "%d\n", rc2); - }else if( isOk==2 ){ - raw_printf(p->out, "0x%08x\n", rc2); - } - }else -#endif /* !defined(SQLITE_UNTESTABLE) */ - - if( c=='t' && n>4 && strncmp(azArg[0], "timeout", n)==0 ){ - open_db(p, 0); - sqlite3_busy_timeout(p->db, nArg>=2 ? (int)integerValue(azArg[1]) : 0); - }else - - if( c=='t' && n>=5 && strncmp(azArg[0], "timer", n)==0 ){ - if( nArg==2 ){ - enableTimer = booleanValue(azArg[1]); - if( enableTimer && !HAS_TIMER ){ - raw_printf(stderr, "Error: timer not available on this system.\n"); - enableTimer = 0; - } - }else{ - raw_printf(stderr, "Usage: .timer on|off\n"); - rc = 1; - } - }else - -#ifndef SQLITE_OMIT_TRACE - if( c=='t' && strncmp(azArg[0], "trace", n)==0 ){ - int mType = 0; - int jj; - open_db(p, 0); - for(jj=1; jjeTraceType = SHELL_TRACE_EXPANDED; - } -#ifdef SQLITE_ENABLE_NORMALIZE - else if( optionMatch(z, "normalized") ){ - p->eTraceType = SHELL_TRACE_NORMALIZED; - } -#endif - else if( optionMatch(z, "plain") ){ - p->eTraceType = SHELL_TRACE_PLAIN; - } - else if( optionMatch(z, "profile") ){ - mType |= SQLITE_TRACE_PROFILE; - } - else if( optionMatch(z, "row") ){ - mType |= SQLITE_TRACE_ROW; - } - else if( optionMatch(z, "stmt") ){ - mType |= SQLITE_TRACE_STMT; - } - else if( optionMatch(z, "close") ){ - mType |= SQLITE_TRACE_CLOSE; - } - else { - raw_printf(stderr, "Unknown option \"%s\" on \".trace\"\n", z); - rc = 1; - goto meta_command_exit; - } - }else{ - output_file_close(p->traceOut); - p->traceOut = output_file_open(azArg[1], 0); - } - } - if( p->traceOut==0 ){ - sqlite3_trace_v2(p->db, 0, 0, 0); - }else{ - if( mType==0 ) mType = SQLITE_TRACE_STMT; - sqlite3_trace_v2(p->db, mType, sql_trace_callback, p); - } - }else -#endif /* !defined(SQLITE_OMIT_TRACE) */ - -#if defined(SQLITE_DEBUG) && !defined(SQLITE_OMIT_VIRTUALTABLE) - if( c=='u' && strncmp(azArg[0], "unmodule", n)==0 ){ - int ii; - int lenOpt; - char *zOpt; - if( nArg<2 ){ - raw_printf(stderr, "Usage: .unmodule [--allexcept] NAME ...\n"); - rc = 1; - goto meta_command_exit; - } - open_db(p, 0); - zOpt = azArg[1]; - if( zOpt[0]=='-' && zOpt[1]=='-' && zOpt[2]!=0 ) zOpt++; - lenOpt = (int)strlen(zOpt); - if( lenOpt>=3 && strncmp(zOpt, "-allexcept",lenOpt)==0 ){ - assert( azArg[nArg]==0 ); - sqlite3_drop_modules(p->db, nArg>2 ? (const char**)(azArg+2) : 0); - }else{ - for(ii=1; iidb, azArg[ii], 0, 0); - } - } - }else -#endif - -#if SQLITE_USER_AUTHENTICATION - if( c=='u' && strncmp(azArg[0], "user", n)==0 ){ - if( nArg<2 ){ - raw_printf(stderr, "Usage: .user SUBCOMMAND ...\n"); - rc = 1; - goto meta_command_exit; - } - open_db(p, 0); - if( strcmp(azArg[1],"login")==0 ){ - if( nArg!=4 ){ - raw_printf(stderr, "Usage: .user login USER PASSWORD\n"); - rc = 1; - goto meta_command_exit; - } - rc = sqlite3_user_authenticate(p->db, azArg[2], azArg[3], - strlen30(azArg[3])); - if( rc ){ - utf8_printf(stderr, "Authentication failed for user %s\n", azArg[2]); - rc = 1; - } - }else if( strcmp(azArg[1],"add")==0 ){ - if( nArg!=5 ){ - raw_printf(stderr, "Usage: .user add USER PASSWORD ISADMIN\n"); - rc = 1; - goto meta_command_exit; - } - rc = sqlite3_user_add(p->db, azArg[2], azArg[3], strlen30(azArg[3]), - booleanValue(azArg[4])); - if( rc ){ - raw_printf(stderr, "User-Add failed: %d\n", rc); - rc = 1; - } - }else if( strcmp(azArg[1],"edit")==0 ){ - if( nArg!=5 ){ - raw_printf(stderr, "Usage: .user edit USER PASSWORD ISADMIN\n"); - rc = 1; - goto meta_command_exit; - } - rc = sqlite3_user_change(p->db, azArg[2], azArg[3], strlen30(azArg[3]), - booleanValue(azArg[4])); - if( rc ){ - raw_printf(stderr, "User-Edit failed: %d\n", rc); - rc = 1; - } - }else if( strcmp(azArg[1],"delete")==0 ){ - if( nArg!=3 ){ - raw_printf(stderr, "Usage: .user delete USER\n"); - rc = 1; - goto meta_command_exit; - } - rc = sqlite3_user_delete(p->db, azArg[2]); - if( rc ){ - raw_printf(stderr, "User-Delete failed: %d\n", rc); - rc = 1; - } - }else{ - raw_printf(stderr, "Usage: .user login|add|edit|delete ...\n"); - rc = 1; - goto meta_command_exit; - } - }else -#endif /* SQLITE_USER_AUTHENTICATION */ - - if( c=='v' && strncmp(azArg[0], "version", n)==0 ){ - utf8_printf(p->out, "SQLite %s %s\n" /*extra-version-info*/, - sqlite3_libversion(), sqlite3_sourceid()); -#if SQLITE_HAVE_ZLIB - utf8_printf(p->out, "zlib version %s\n", zlibVersion()); -#endif -#define CTIMEOPT_VAL_(opt) #opt -#define CTIMEOPT_VAL(opt) CTIMEOPT_VAL_(opt) -#if defined(__clang__) && defined(__clang_major__) - utf8_printf(p->out, "clang-" CTIMEOPT_VAL(__clang_major__) "." - CTIMEOPT_VAL(__clang_minor__) "." - CTIMEOPT_VAL(__clang_patchlevel__) "\n"); -#elif defined(_MSC_VER) - utf8_printf(p->out, "msvc-" CTIMEOPT_VAL(_MSC_VER) "\n"); -#elif defined(__GNUC__) && defined(__VERSION__) - utf8_printf(p->out, "gcc-" __VERSION__ "\n"); -#endif - }else - - if( c=='v' && strncmp(azArg[0], "vfsinfo", n)==0 ){ - const char *zDbName = nArg==2 ? azArg[1] : "main"; - sqlite3_vfs *pVfs = 0; - if( p->db ){ - sqlite3_file_control(p->db, zDbName, SQLITE_FCNTL_VFS_POINTER, &pVfs); - if( pVfs ){ - utf8_printf(p->out, "vfs.zName = \"%s\"\n", pVfs->zName); - raw_printf(p->out, "vfs.iVersion = %d\n", pVfs->iVersion); - raw_printf(p->out, "vfs.szOsFile = %d\n", pVfs->szOsFile); - raw_printf(p->out, "vfs.mxPathname = %d\n", pVfs->mxPathname); - } - } - }else - - if( c=='v' && strncmp(azArg[0], "vfslist", n)==0 ){ - sqlite3_vfs *pVfs; - sqlite3_vfs *pCurrent = 0; - if( p->db ){ - sqlite3_file_control(p->db, "main", SQLITE_FCNTL_VFS_POINTER, &pCurrent); - } - for(pVfs=sqlite3_vfs_find(0); pVfs; pVfs=pVfs->pNext){ - utf8_printf(p->out, "vfs.zName = \"%s\"%s\n", pVfs->zName, - pVfs==pCurrent ? " <--- CURRENT" : ""); - raw_printf(p->out, "vfs.iVersion = %d\n", pVfs->iVersion); - raw_printf(p->out, "vfs.szOsFile = %d\n", pVfs->szOsFile); - raw_printf(p->out, "vfs.mxPathname = %d\n", pVfs->mxPathname); - if( pVfs->pNext ){ - raw_printf(p->out, "-----------------------------------\n"); - } - } - }else - - if( c=='v' && strncmp(azArg[0], "vfsname", n)==0 ){ - const char *zDbName = nArg==2 ? azArg[1] : "main"; - char *zVfsName = 0; - if( p->db ){ - sqlite3_file_control(p->db, zDbName, SQLITE_FCNTL_VFSNAME, &zVfsName); - if( zVfsName ){ - utf8_printf(p->out, "%s\n", zVfsName); - sqlite3_free(zVfsName); - } - } - }else - -#if defined(SQLITE_DEBUG) && defined(SQLITE_ENABLE_WHERETRACE) - if( c=='w' && strncmp(azArg[0], "wheretrace", n)==0 ){ - sqlite3WhereTrace = nArg>=2 ? booleanValue(azArg[1]) : 0xff; - }else -#endif - - if( c=='w' && strncmp(azArg[0], "width", n)==0 ){ - int j; - assert( nArg<=ArraySize(azArg) ); - p->nWidth = nArg-1; - p->colWidth = realloc(p->colWidth, p->nWidth*sizeof(int)*2); - if( p->colWidth==0 && p->nWidth>0 ) shell_out_of_memory(); - if( p->nWidth ) p->actualWidth = &p->colWidth[p->nWidth]; - for(j=1; jcolWidth[j-1] = (int)integerValue(azArg[j]); - } - }else - - { - utf8_printf(stderr, "Error: unknown command or invalid arguments: " - " \"%s\". Enter \".help\" for help\n", azArg[0]); - rc = 1; - } - -meta_command_exit: - if( p->outCount ){ - p->outCount--; - if( p->outCount==0 ) output_reset(p); - } - return rc; -} - -/* -** Return TRUE if a semicolon occurs anywhere in the first N characters -** of string z[]. -*/ -static int line_contains_semicolon(const char *z, int N){ - int i; - for(i=0; iflgProgress & SHELL_PROGRESS_RESET ) p->nProgress = 0; - BEGIN_TIMER; - rc = shell_exec(p, zSql, &zErrMsg); - END_TIMER; - if( rc || zErrMsg ){ - char zPrefix[100]; - if( in!=0 || !stdin_is_interactive ){ - sqlite3_snprintf(sizeof(zPrefix), zPrefix, - "Error: near line %d:", startline); - }else{ - sqlite3_snprintf(sizeof(zPrefix), zPrefix, "Error:"); - } - if( zErrMsg!=0 ){ - utf8_printf(stderr, "%s %s\n", zPrefix, zErrMsg); - sqlite3_free(zErrMsg); - zErrMsg = 0; - }else{ - utf8_printf(stderr, "%s %s\n", zPrefix, sqlite3_errmsg(p->db)); - } - return 1; - }else if( ShellHasFlag(p, SHFLG_CountChanges) ){ - raw_printf(p->out, "changes: %3d total_changes: %d\n", - sqlite3_changes(p->db), sqlite3_total_changes(p->db)); - } - return 0; -} - - -/* -** Read input from *in and process it. If *in==0 then input -** is interactive - the user is typing it it. Otherwise, input -** is coming from a file or device. A prompt is issued and history -** is saved only if input is interactive. An interrupt signal will -** cause this routine to exit immediately, unless input is interactive. -** -** Return the number of errors. -*/ -static int process_input(ShellState *p){ - char *zLine = 0; /* A single input line */ - char *zSql = 0; /* Accumulated SQL text */ - int nLine; /* Length of current line */ - int nSql = 0; /* Bytes of zSql[] used */ - int nAlloc = 0; /* Allocated zSql[] space */ - int nSqlPrior = 0; /* Bytes of zSql[] used by prior line */ - int rc; /* Error code */ - int errCnt = 0; /* Number of errors seen */ - int startline = 0; /* Line number for start of current input */ - - p->lineno = 0; - while( errCnt==0 || !bail_on_error || (p->in==0 && stdin_is_interactive) ){ - fflush(p->out); - zLine = one_input_line(p->in, zLine, nSql>0); - if( zLine==0 ){ - /* End of input */ - if( p->in==0 && stdin_is_interactive ) printf("\n"); - break; - } - if( seenInterrupt ){ - if( p->in!=0 ) break; - seenInterrupt = 0; - } - p->lineno++; - if( nSql==0 && _all_whitespace(zLine) ){ - if( ShellHasFlag(p, SHFLG_Echo) ) printf("%s\n", zLine); - continue; - } - if( zLine && (zLine[0]=='.' || zLine[0]=='#') && nSql==0 ){ - if( ShellHasFlag(p, SHFLG_Echo) ) printf("%s\n", zLine); - if( zLine[0]=='.' ){ - rc = do_meta_command(zLine, p); - if( rc==2 ){ /* exit requested */ - break; - }else if( rc ){ - errCnt++; - } - } - continue; - } - if( line_is_command_terminator(zLine) && line_is_complete(zSql, nSql) ){ - memcpy(zLine,";",2); - } - nLine = strlen30(zLine); - if( nSql+nLine+2>=nAlloc ){ - nAlloc = nSql+nLine+100; - zSql = realloc(zSql, nAlloc); - if( zSql==0 ) shell_out_of_memory(); - } - nSqlPrior = nSql; - if( nSql==0 ){ - int i; - for(i=0; zLine[i] && IsSpace(zLine[i]); i++){} - assert( nAlloc>0 && zSql!=0 ); - memcpy(zSql, zLine+i, nLine+1-i); - startline = p->lineno; - nSql = nLine-i; - }else{ - zSql[nSql++] = '\n'; - memcpy(zSql+nSql, zLine, nLine+1); - nSql += nLine; - } - if( nSql && line_contains_semicolon(&zSql[nSqlPrior], nSql-nSqlPrior) - && sqlite3_complete(zSql) ){ - errCnt += runOneSqlLine(p, zSql, p->in, startline); - nSql = 0; - if( p->outCount ){ - output_reset(p); - p->outCount = 0; - }else{ - clearTempFile(p); - } - }else if( nSql && _all_whitespace(zSql) ){ - if( ShellHasFlag(p, SHFLG_Echo) ) printf("%s\n", zSql); - nSql = 0; - } - } - if( nSql && !_all_whitespace(zSql) ){ - errCnt += runOneSqlLine(p, zSql, p->in, startline); - } - free(zSql); - free(zLine); - return errCnt>0; -} - -/* -** Return a pathname which is the user's home directory. A -** 0 return indicates an error of some kind. -*/ -static char *find_home_dir(int clearFlag){ - static char *home_dir = NULL; - if( clearFlag ){ - free(home_dir); - home_dir = 0; - return 0; - } - if( home_dir ) return home_dir; - -#if !defined(_WIN32) && !defined(WIN32) && !defined(_WIN32_WCE) \ - && !defined(__RTP__) && !defined(_WRS_KERNEL) - { - struct passwd *pwent; - uid_t uid = getuid(); - if( (pwent=getpwuid(uid)) != NULL) { - home_dir = pwent->pw_dir; - } - } -#endif - -#if defined(_WIN32_WCE) - /* Windows CE (arm-wince-mingw32ce-gcc) does not provide getenv() - */ - home_dir = "/"; -#else - -#if defined(_WIN32) || defined(WIN32) - if (!home_dir) { - home_dir = getenv("USERPROFILE"); - } -#endif - - if (!home_dir) { - home_dir = getenv("HOME"); - } - -#if defined(_WIN32) || defined(WIN32) - if (!home_dir) { - char *zDrive, *zPath; - int n; - zDrive = getenv("HOMEDRIVE"); - zPath = getenv("HOMEPATH"); - if( zDrive && zPath ){ - n = strlen30(zDrive) + strlen30(zPath) + 1; - home_dir = malloc( n ); - if( home_dir==0 ) return 0; - sqlite3_snprintf(n, home_dir, "%s%s", zDrive, zPath); - return home_dir; - } - home_dir = "c:\\"; - } -#endif - -#endif /* !_WIN32_WCE */ - - if( home_dir ){ - int n = strlen30(home_dir) + 1; - char *z = malloc( n ); - if( z ) memcpy(z, home_dir, n); - home_dir = z; - } - - return home_dir; -} - -/* -** Read input from the file given by sqliterc_override. Or if that -** parameter is NULL, take input from ~/.sqliterc -** -** Returns the number of errors. -*/ -static void process_sqliterc( - ShellState *p, /* Configuration data */ - const char *sqliterc_override /* Name of config file. NULL to use default */ -){ - char *home_dir = NULL; - const char *sqliterc = sqliterc_override; - char *zBuf = 0; - FILE *inSaved = p->in; - int savedLineno = p->lineno; - - if (sqliterc == NULL) { - home_dir = find_home_dir(0); - if( home_dir==0 ){ - raw_printf(stderr, "-- warning: cannot find home directory;" - " cannot read ~/.sqliterc\n"); - return; - } - zBuf = sqlite3_mprintf("%s/.sqliterc",home_dir); - sqliterc = zBuf; - } - p->in = fopen(sqliterc,"rb"); - if( p->in ){ - if( stdin_is_interactive ){ - utf8_printf(stderr,"-- Loading resources from %s\n",sqliterc); - } - process_input(p); - fclose(p->in); - } - p->in = inSaved; - p->lineno = savedLineno; - sqlite3_free(zBuf); -} - -/* -** Show available command line options -*/ -static const char zOptions[] = -#if defined(SQLITE_HAVE_ZLIB) && !defined(SQLITE_OMIT_VIRTUALTABLE) - " -A ARGS... run \".archive ARGS\" and exit\n" -#endif - " -append append the database to the end of the file\n" - " -ascii set output mode to 'ascii'\n" - " -bail stop after hitting an error\n" - " -batch force batch I/O\n" - " -box set output mode to 'box'\n" - " -column set output mode to 'column'\n" - " -cmd COMMAND run \"COMMAND\" before reading stdin\n" - " -csv set output mode to 'csv'\n" -#if defined(SQLITE_ENABLE_DESERIALIZE) - " -deserialize open the database using sqlite3_deserialize()\n" -#endif - " -echo print commands before execution\n" - " -init FILENAME read/process named file\n" - " -[no]header turn headers on or off\n" -#if defined(SQLITE_ENABLE_MEMSYS3) || defined(SQLITE_ENABLE_MEMSYS5) - " -heap SIZE Size of heap for memsys3 or memsys5\n" -#endif - " -help show this message\n" - " -html set output mode to HTML\n" - " -interactive force interactive I/O\n" - " -json set output mode to 'json'\n" - " -line set output mode to 'line'\n" - " -list set output mode to 'list'\n" - " -lookaside SIZE N use N entries of SZ bytes for lookaside memory\n" - " -markdown set output mode to 'markdown'\n" -#if defined(SQLITE_ENABLE_DESERIALIZE) - " -maxsize N maximum size for a --deserialize database\n" -#endif - " -memtrace trace all memory allocations and deallocations\n" - " -mmap N default mmap size set to N\n" -#ifdef SQLITE_ENABLE_MULTIPLEX - " -multiplex enable the multiplexor VFS\n" -#endif - " -newline SEP set output row separator. Default: '\\n'\n" - " -nofollow refuse to open symbolic links to database files\n" - " -nullvalue TEXT set text string for NULL values. Default ''\n" - " -pagecache SIZE N use N slots of SZ bytes each for page cache memory\n" - " -quote set output mode to 'quote'\n" - " -readonly open the database read-only\n" - " -separator SEP set output column separator. Default: '|'\n" -#ifdef SQLITE_ENABLE_SORTER_REFERENCES - " -sorterref SIZE sorter references threshold size\n" -#endif - " -stats print memory stats before each finalize\n" - " -table set output mode to 'table'\n" - " -version show SQLite version\n" - " -vfs NAME use NAME as the default VFS\n" -#ifdef SQLITE_ENABLE_VFSTRACE - " -vfstrace enable tracing of all VFS calls\n" -#endif -#ifdef SQLITE_HAVE_ZLIB - " -zip open the file as a ZIP Archive\n" -#endif -; -static void usage(int showDetail){ - utf8_printf(stderr, - "Usage: %s [OPTIONS] FILENAME [SQL]\n" - "FILENAME is the name of an SQLite database. A new database is created\n" - "if the file does not previously exist.\n", Argv0); - if( showDetail ){ - utf8_printf(stderr, "OPTIONS include:\n%s", zOptions); - }else{ - raw_printf(stderr, "Use the -help option for additional information\n"); - } - exit(1); -} - -/* -** Internal check: Verify that the SQLite is uninitialized. Print a -** error message if it is initialized. -*/ -static void verify_uninitialized(void){ - if( sqlite3_config(-1)==SQLITE_MISUSE ){ - utf8_printf(stdout, "WARNING: attempt to configure SQLite after" - " initialization.\n"); - } -} - -/* -** Initialize the state information in data -*/ -static void main_init(ShellState *data) { - memset(data, 0, sizeof(*data)); - data->normalMode = data->cMode = data->mode = MODE_List; - data->autoExplain = 1; - memcpy(data->colSeparator,SEP_Column, 2); - memcpy(data->rowSeparator,SEP_Row, 2); - data->showHeader = 0; - data->shellFlgs = SHFLG_Lookaside; - verify_uninitialized(); - sqlite3_config(SQLITE_CONFIG_URI, 1); - sqlite3_config(SQLITE_CONFIG_LOG, shellLog, data); - sqlite3_config(SQLITE_CONFIG_MULTITHREAD); - sqlite3_snprintf(sizeof(mainPrompt), mainPrompt,"sqlite> "); - sqlite3_snprintf(sizeof(continuePrompt), continuePrompt," ...> "); -} - -/* -** Output text to the console in a font that attracts extra attention. -*/ -#ifdef _WIN32 -static void printBold(const char *zText){ -#if !SQLITE_OS_WINRT - HANDLE out = GetStdHandle(STD_OUTPUT_HANDLE); - CONSOLE_SCREEN_BUFFER_INFO defaultScreenInfo; - GetConsoleScreenBufferInfo(out, &defaultScreenInfo); - SetConsoleTextAttribute(out, - FOREGROUND_RED|FOREGROUND_INTENSITY - ); -#endif - printf("%s", zText); -#if !SQLITE_OS_WINRT - SetConsoleTextAttribute(out, defaultScreenInfo.wAttributes); -#endif -} -#else -static void printBold(const char *zText){ - printf("\033[1m%s\033[0m", zText); -} -#endif - -/* -** Get the argument to an --option. Throw an error and die if no argument -** is available. -*/ -static char *cmdline_option_value(int argc, char **argv, int i){ - if( i==argc ){ - utf8_printf(stderr, "%s: Error: missing argument to %s\n", - argv[0], argv[argc-1]); - exit(1); - } - return argv[i]; -} - -#ifndef SQLITE_SHELL_IS_UTF8 -# if (defined(_WIN32) || defined(WIN32)) && defined(_MSC_VER) -# define SQLITE_SHELL_IS_UTF8 (0) -# else -# define SQLITE_SHELL_IS_UTF8 (1) -# endif -#endif - -#if SQLITE_SHELL_IS_UTF8 -int SQLITE_CDECL main(int argc, char **argv){ -#else -int SQLITE_CDECL wmain(int argc, wchar_t **wargv){ - char **argv; -#endif - char *zErrMsg = 0; - ShellState data; - const char *zInitFile = 0; - int i; - int rc = 0; - int warnInmemoryDb = 0; - int readStdin = 1; - int nCmd = 0; - char **azCmd = 0; - const char *zVfs = 0; /* Value of -vfs command-line option */ -#if !SQLITE_SHELL_IS_UTF8 - char **argvToFree = 0; - int argcToFree = 0; -#endif - - setBinaryMode(stdin, 0); - setvbuf(stderr, 0, _IONBF, 0); /* Make sure stderr is unbuffered */ - stdin_is_interactive = isatty(0); - stdout_is_console = isatty(1); - -#ifdef SQLITE_DEBUG - registerOomSimulator(); -#endif - -#if !defined(_WIN32_WCE) - if( getenv("SQLITE_DEBUG_BREAK") ){ - if( isatty(0) && isatty(2) ){ - fprintf(stderr, - "attach debugger to process %d and press any key to continue.\n", - GETPID()); - fgetc(stdin); - }else{ -#if defined(_WIN32) || defined(WIN32) -#if SQLITE_OS_WINRT - __debugbreak(); -#else - DebugBreak(); -#endif -#elif defined(SIGTRAP) - raise(SIGTRAP); -#endif - } - } -#endif - -#if USE_SYSTEM_SQLITE+0!=1 - if( strncmp(sqlite3_sourceid(),SQLITE_SOURCE_ID,60)!=0 ){ - utf8_printf(stderr, "SQLite header and source version mismatch\n%s\n%s\n", - sqlite3_sourceid(), SQLITE_SOURCE_ID); - exit(1); - } -#endif - main_init(&data); - - /* On Windows, we must translate command-line arguments into UTF-8. - ** The SQLite memory allocator subsystem has to be enabled in order to - ** do this. But we want to run an sqlite3_shutdown() afterwards so that - ** subsequent sqlite3_config() calls will work. So copy all results into - ** memory that does not come from the SQLite memory allocator. - */ -#if !SQLITE_SHELL_IS_UTF8 - sqlite3_initialize(); - argvToFree = malloc(sizeof(argv[0])*argc*2); - argcToFree = argc; - argv = argvToFree + argc; - if( argv==0 ) shell_out_of_memory(); - for(i=0; i=1 && argv && argv[0] ); - Argv0 = argv[0]; - - /* Make sure we have a valid signal handler early, before anything - ** else is done. - */ -#ifdef SIGINT - signal(SIGINT, interrupt_handler); -#elif (defined(_WIN32) || defined(WIN32)) && !defined(_WIN32_WCE) - SetConsoleCtrlHandler(ConsoleCtrlHandler, TRUE); -#endif - -#ifdef SQLITE_SHELL_DBNAME_PROC - { - /* If the SQLITE_SHELL_DBNAME_PROC macro is defined, then it is the name - ** of a C-function that will provide the name of the database file. Use - ** this compile-time option to embed this shell program in larger - ** applications. */ - extern void SQLITE_SHELL_DBNAME_PROC(const char**); - SQLITE_SHELL_DBNAME_PROC(&data.zDbFilename); - warnInmemoryDb = 0; - } -#endif - - /* Do an initial pass through the command-line argument to locate - ** the name of the database file, the name of the initialization file, - ** the size of the alternative malloc heap, - ** and the first command to execute. - */ - verify_uninitialized(); - for(i=1; i0x7fff0000 ) szHeap = 0x7fff0000; - sqlite3_config(SQLITE_CONFIG_HEAP, malloc((int)szHeap), (int)szHeap, 64); -#else - (void)cmdline_option_value(argc, argv, ++i); -#endif - }else if( strcmp(z,"-pagecache")==0 ){ - int n, sz; - sz = (int)integerValue(cmdline_option_value(argc,argv,++i)); - if( sz>70000 ) sz = 70000; - if( sz<0 ) sz = 0; - n = (int)integerValue(cmdline_option_value(argc,argv,++i)); - sqlite3_config(SQLITE_CONFIG_PAGECACHE, - (n>0 && sz>0) ? malloc(n*sz) : 0, sz, n); - data.shellFlgs |= SHFLG_Pagecache; - }else if( strcmp(z,"-lookaside")==0 ){ - int n, sz; - sz = (int)integerValue(cmdline_option_value(argc,argv,++i)); - if( sz<0 ) sz = 0; - n = (int)integerValue(cmdline_option_value(argc,argv,++i)); - if( n<0 ) n = 0; - sqlite3_config(SQLITE_CONFIG_LOOKASIDE, sz, n); - if( sz*n==0 ) data.shellFlgs &= ~SHFLG_Lookaside; -#ifdef SQLITE_ENABLE_VFSTRACE - }else if( strcmp(z,"-vfstrace")==0 ){ - extern int vfstrace_register( - const char *zTraceName, - const char *zOldVfsName, - int (*xOut)(const char*,void*), - void *pOutArg, - int makeDefault - ); - vfstrace_register("trace",0,(int(*)(const char*,void*))fputs,stderr,1); -#endif -#ifdef SQLITE_ENABLE_MULTIPLEX - }else if( strcmp(z,"-multiplex")==0 ){ - extern int sqlite3_multiple_initialize(const char*,int); - sqlite3_multiplex_initialize(0, 1); -#endif - }else if( strcmp(z,"-mmap")==0 ){ - sqlite3_int64 sz = integerValue(cmdline_option_value(argc,argv,++i)); - sqlite3_config(SQLITE_CONFIG_MMAP_SIZE, sz, sz); -#ifdef SQLITE_ENABLE_SORTER_REFERENCES - }else if( strcmp(z,"-sorterref")==0 ){ - sqlite3_int64 sz = integerValue(cmdline_option_value(argc,argv,++i)); - sqlite3_config(SQLITE_CONFIG_SORTERREF_SIZE, (int)sz); -#endif - }else if( strcmp(z,"-vfs")==0 ){ - zVfs = cmdline_option_value(argc, argv, ++i); -#ifdef SQLITE_HAVE_ZLIB - }else if( strcmp(z,"-zip")==0 ){ - data.openMode = SHELL_OPEN_ZIPFILE; -#endif - }else if( strcmp(z,"-append")==0 ){ - data.openMode = SHELL_OPEN_APPENDVFS; -#ifdef SQLITE_ENABLE_DESERIALIZE - }else if( strcmp(z,"-deserialize")==0 ){ - data.openMode = SHELL_OPEN_DESERIALIZE; - }else if( strcmp(z,"-maxsize")==0 && i+10 ){ - utf8_printf(stderr, "Error: cannot mix regular SQL or dot-commands" - " with \"%s\"\n", z); - return 1; - } - open_db(&data, OPEN_DB_ZIPFILE); - if( z[2] ){ - argv[i] = &z[2]; - arDotCommand(&data, 1, argv+(i-1), argc-(i-1)); - }else{ - arDotCommand(&data, 1, argv+i, argc-i); - } - readStdin = 0; - break; -#endif - }else{ - utf8_printf(stderr,"%s: Error: unknown option: %s\n", Argv0, z); - raw_printf(stderr,"Use -help for a list of options.\n"); - return 1; - } - data.cMode = data.mode; - } - - if( !readStdin ){ - /* Run all arguments that do not begin with '-' as if they were separate - ** command-line inputs, except for the argToSkip argument which contains - ** the database filename. - */ - for(i=0; iaggregate_context -#ifndef SQLITE_OMIT_DEPRECATED -#define sqlite3_aggregate_count sqlite3_api->aggregate_count -#endif -#define sqlite3_bind_blob sqlite3_api->bind_blob -#define sqlite3_bind_double sqlite3_api->bind_double -#define sqlite3_bind_int sqlite3_api->bind_int -#define sqlite3_bind_int64 sqlite3_api->bind_int64 -#define sqlite3_bind_null sqlite3_api->bind_null -#define sqlite3_bind_parameter_count sqlite3_api->bind_parameter_count -#define sqlite3_bind_parameter_index sqlite3_api->bind_parameter_index -#define sqlite3_bind_parameter_name sqlite3_api->bind_parameter_name -#define sqlite3_bind_text sqlite3_api->bind_text -#define sqlite3_bind_text16 sqlite3_api->bind_text16 -#define sqlite3_bind_value sqlite3_api->bind_value -#define sqlite3_busy_handler sqlite3_api->busy_handler -#define sqlite3_busy_timeout sqlite3_api->busy_timeout -#define sqlite3_changes sqlite3_api->changes -#define sqlite3_close sqlite3_api->close -#define sqlite3_collation_needed sqlite3_api->collation_needed -#define sqlite3_collation_needed16 sqlite3_api->collation_needed16 -#define sqlite3_column_blob sqlite3_api->column_blob -#define sqlite3_column_bytes sqlite3_api->column_bytes -#define sqlite3_column_bytes16 sqlite3_api->column_bytes16 -#define sqlite3_column_count sqlite3_api->column_count -#define sqlite3_column_database_name sqlite3_api->column_database_name -#define sqlite3_column_database_name16 sqlite3_api->column_database_name16 -#define sqlite3_column_decltype sqlite3_api->column_decltype -#define sqlite3_column_decltype16 sqlite3_api->column_decltype16 -#define sqlite3_column_double sqlite3_api->column_double -#define sqlite3_column_int sqlite3_api->column_int -#define sqlite3_column_int64 sqlite3_api->column_int64 -#define sqlite3_column_name sqlite3_api->column_name -#define sqlite3_column_name16 sqlite3_api->column_name16 -#define sqlite3_column_origin_name sqlite3_api->column_origin_name -#define sqlite3_column_origin_name16 sqlite3_api->column_origin_name16 -#define sqlite3_column_table_name sqlite3_api->column_table_name -#define sqlite3_column_table_name16 sqlite3_api->column_table_name16 -#define sqlite3_column_text sqlite3_api->column_text -#define sqlite3_column_text16 sqlite3_api->column_text16 -#define sqlite3_column_type sqlite3_api->column_type -#define sqlite3_column_value sqlite3_api->column_value -#define sqlite3_commit_hook sqlite3_api->commit_hook -#define sqlite3_complete sqlite3_api->complete -#define sqlite3_complete16 sqlite3_api->complete16 -#define sqlite3_create_collation sqlite3_api->create_collation -#define sqlite3_create_collation16 sqlite3_api->create_collation16 -#define sqlite3_create_function sqlite3_api->create_function -#define sqlite3_create_function16 sqlite3_api->create_function16 -#define sqlite3_create_module sqlite3_api->create_module -#define sqlite3_create_module_v2 sqlite3_api->create_module_v2 -#define sqlite3_data_count sqlite3_api->data_count -#define sqlite3_db_handle sqlite3_api->db_handle -#define sqlite3_declare_vtab sqlite3_api->declare_vtab -#define sqlite3_enable_shared_cache sqlite3_api->enable_shared_cache -#define sqlite3_errcode sqlite3_api->errcode -#define sqlite3_errmsg sqlite3_api->errmsg -#define sqlite3_errmsg16 sqlite3_api->errmsg16 -#define sqlite3_exec sqlite3_api->exec -#ifndef SQLITE_OMIT_DEPRECATED -#define sqlite3_expired sqlite3_api->expired -#endif -#define sqlite3_finalize sqlite3_api->finalize -#define sqlite3_free sqlite3_api->free -#define sqlite3_free_table sqlite3_api->free_table -#define sqlite3_get_autocommit sqlite3_api->get_autocommit -#define sqlite3_get_auxdata sqlite3_api->get_auxdata -#define sqlite3_get_table sqlite3_api->get_table -#ifndef SQLITE_OMIT_DEPRECATED -#define sqlite3_global_recover sqlite3_api->global_recover -#endif -#define sqlite3_interrupt sqlite3_api->interruptx -#define sqlite3_last_insert_rowid sqlite3_api->last_insert_rowid -#define sqlite3_libversion sqlite3_api->libversion -#define sqlite3_libversion_number sqlite3_api->libversion_number -#define sqlite3_malloc sqlite3_api->malloc -#define sqlite3_mprintf sqlite3_api->mprintf -#define sqlite3_open sqlite3_api->open -#define sqlite3_open16 sqlite3_api->open16 -#define sqlite3_prepare sqlite3_api->prepare -#define sqlite3_prepare16 sqlite3_api->prepare16 -#define sqlite3_prepare_v2 sqlite3_api->prepare_v2 -#define sqlite3_prepare16_v2 sqlite3_api->prepare16_v2 -#define sqlite3_profile sqlite3_api->profile -#define sqlite3_progress_handler sqlite3_api->progress_handler -#define sqlite3_realloc sqlite3_api->realloc -#define sqlite3_reset sqlite3_api->reset -#define sqlite3_result_blob sqlite3_api->result_blob -#define sqlite3_result_double sqlite3_api->result_double -#define sqlite3_result_error sqlite3_api->result_error -#define sqlite3_result_error16 sqlite3_api->result_error16 -#define sqlite3_result_int sqlite3_api->result_int -#define sqlite3_result_int64 sqlite3_api->result_int64 -#define sqlite3_result_null sqlite3_api->result_null -#define sqlite3_result_text sqlite3_api->result_text -#define sqlite3_result_text16 sqlite3_api->result_text16 -#define sqlite3_result_text16be sqlite3_api->result_text16be -#define sqlite3_result_text16le sqlite3_api->result_text16le -#define sqlite3_result_value sqlite3_api->result_value -#define sqlite3_rollback_hook sqlite3_api->rollback_hook -#define sqlite3_set_authorizer sqlite3_api->set_authorizer -#define sqlite3_set_auxdata sqlite3_api->set_auxdata -#define sqlite3_snprintf sqlite3_api->xsnprintf -#define sqlite3_step sqlite3_api->step -#define sqlite3_table_column_metadata sqlite3_api->table_column_metadata -#define sqlite3_thread_cleanup sqlite3_api->thread_cleanup -#define sqlite3_total_changes sqlite3_api->total_changes -#define sqlite3_trace sqlite3_api->trace -#ifndef SQLITE_OMIT_DEPRECATED -#define sqlite3_transfer_bindings sqlite3_api->transfer_bindings -#endif -#define sqlite3_update_hook sqlite3_api->update_hook -#define sqlite3_user_data sqlite3_api->user_data -#define sqlite3_value_blob sqlite3_api->value_blob -#define sqlite3_value_bytes sqlite3_api->value_bytes -#define sqlite3_value_bytes16 sqlite3_api->value_bytes16 -#define sqlite3_value_double sqlite3_api->value_double -#define sqlite3_value_int sqlite3_api->value_int -#define sqlite3_value_int64 sqlite3_api->value_int64 -#define sqlite3_value_numeric_type sqlite3_api->value_numeric_type -#define sqlite3_value_text sqlite3_api->value_text -#define sqlite3_value_text16 sqlite3_api->value_text16 -#define sqlite3_value_text16be sqlite3_api->value_text16be -#define sqlite3_value_text16le sqlite3_api->value_text16le -#define sqlite3_value_type sqlite3_api->value_type -#define sqlite3_vmprintf sqlite3_api->vmprintf -#define sqlite3_vsnprintf sqlite3_api->xvsnprintf -#define sqlite3_overload_function sqlite3_api->overload_function -#define sqlite3_prepare_v2 sqlite3_api->prepare_v2 -#define sqlite3_prepare16_v2 sqlite3_api->prepare16_v2 -#define sqlite3_clear_bindings sqlite3_api->clear_bindings -#define sqlite3_bind_zeroblob sqlite3_api->bind_zeroblob -#define sqlite3_blob_bytes sqlite3_api->blob_bytes -#define sqlite3_blob_close sqlite3_api->blob_close -#define sqlite3_blob_open sqlite3_api->blob_open -#define sqlite3_blob_read sqlite3_api->blob_read -#define sqlite3_blob_write sqlite3_api->blob_write -#define sqlite3_create_collation_v2 sqlite3_api->create_collation_v2 -#define sqlite3_file_control sqlite3_api->file_control -#define sqlite3_memory_highwater sqlite3_api->memory_highwater -#define sqlite3_memory_used sqlite3_api->memory_used -#define sqlite3_mutex_alloc sqlite3_api->mutex_alloc -#define sqlite3_mutex_enter sqlite3_api->mutex_enter -#define sqlite3_mutex_free sqlite3_api->mutex_free -#define sqlite3_mutex_leave sqlite3_api->mutex_leave -#define sqlite3_mutex_try sqlite3_api->mutex_try -#define sqlite3_open_v2 sqlite3_api->open_v2 -#define sqlite3_release_memory sqlite3_api->release_memory -#define sqlite3_result_error_nomem sqlite3_api->result_error_nomem -#define sqlite3_result_error_toobig sqlite3_api->result_error_toobig -#define sqlite3_sleep sqlite3_api->sleep -#define sqlite3_soft_heap_limit sqlite3_api->soft_heap_limit -#define sqlite3_vfs_find sqlite3_api->vfs_find -#define sqlite3_vfs_register sqlite3_api->vfs_register -#define sqlite3_vfs_unregister sqlite3_api->vfs_unregister -#define sqlite3_threadsafe sqlite3_api->xthreadsafe -#define sqlite3_result_zeroblob sqlite3_api->result_zeroblob -#define sqlite3_result_error_code sqlite3_api->result_error_code -#define sqlite3_test_control sqlite3_api->test_control -#define sqlite3_randomness sqlite3_api->randomness -#define sqlite3_context_db_handle sqlite3_api->context_db_handle -#define sqlite3_extended_result_codes sqlite3_api->extended_result_codes -#define sqlite3_limit sqlite3_api->limit -#define sqlite3_next_stmt sqlite3_api->next_stmt -#define sqlite3_sql sqlite3_api->sql -#define sqlite3_status sqlite3_api->status -#define sqlite3_backup_finish sqlite3_api->backup_finish -#define sqlite3_backup_init sqlite3_api->backup_init -#define sqlite3_backup_pagecount sqlite3_api->backup_pagecount -#define sqlite3_backup_remaining sqlite3_api->backup_remaining -#define sqlite3_backup_step sqlite3_api->backup_step -#define sqlite3_compileoption_get sqlite3_api->compileoption_get -#define sqlite3_compileoption_used sqlite3_api->compileoption_used -#define sqlite3_create_function_v2 sqlite3_api->create_function_v2 -#define sqlite3_db_config sqlite3_api->db_config -#define sqlite3_db_mutex sqlite3_api->db_mutex -#define sqlite3_db_status sqlite3_api->db_status -#define sqlite3_extended_errcode sqlite3_api->extended_errcode -#define sqlite3_log sqlite3_api->log -#define sqlite3_soft_heap_limit64 sqlite3_api->soft_heap_limit64 -#define sqlite3_sourceid sqlite3_api->sourceid -#define sqlite3_stmt_status sqlite3_api->stmt_status -#define sqlite3_strnicmp sqlite3_api->strnicmp -#define sqlite3_unlock_notify sqlite3_api->unlock_notify -#define sqlite3_wal_autocheckpoint sqlite3_api->wal_autocheckpoint -#define sqlite3_wal_checkpoint sqlite3_api->wal_checkpoint -#define sqlite3_wal_hook sqlite3_api->wal_hook -#define sqlite3_blob_reopen sqlite3_api->blob_reopen -#define sqlite3_vtab_config sqlite3_api->vtab_config -#define sqlite3_vtab_on_conflict sqlite3_api->vtab_on_conflict -/* Version 3.7.16 and later */ -#define sqlite3_close_v2 sqlite3_api->close_v2 -#define sqlite3_db_filename sqlite3_api->db_filename -#define sqlite3_db_readonly sqlite3_api->db_readonly -#define sqlite3_db_release_memory sqlite3_api->db_release_memory -#define sqlite3_errstr sqlite3_api->errstr -#define sqlite3_stmt_busy sqlite3_api->stmt_busy -#define sqlite3_stmt_readonly sqlite3_api->stmt_readonly -#define sqlite3_stricmp sqlite3_api->stricmp -#define sqlite3_uri_boolean sqlite3_api->uri_boolean -#define sqlite3_uri_int64 sqlite3_api->uri_int64 -#define sqlite3_uri_parameter sqlite3_api->uri_parameter -#define sqlite3_uri_vsnprintf sqlite3_api->xvsnprintf -#define sqlite3_wal_checkpoint_v2 sqlite3_api->wal_checkpoint_v2 -/* Version 3.8.7 and later */ -#define sqlite3_auto_extension sqlite3_api->auto_extension -#define sqlite3_bind_blob64 sqlite3_api->bind_blob64 -#define sqlite3_bind_text64 sqlite3_api->bind_text64 -#define sqlite3_cancel_auto_extension sqlite3_api->cancel_auto_extension -#define sqlite3_load_extension sqlite3_api->load_extension -#define sqlite3_malloc64 sqlite3_api->malloc64 -#define sqlite3_msize sqlite3_api->msize -#define sqlite3_realloc64 sqlite3_api->realloc64 -#define sqlite3_reset_auto_extension sqlite3_api->reset_auto_extension -#define sqlite3_result_blob64 sqlite3_api->result_blob64 -#define sqlite3_result_text64 sqlite3_api->result_text64 -#define sqlite3_strglob sqlite3_api->strglob -/* Version 3.8.11 and later */ -#define sqlite3_value_dup sqlite3_api->value_dup -#define sqlite3_value_free sqlite3_api->value_free -#define sqlite3_result_zeroblob64 sqlite3_api->result_zeroblob64 -#define sqlite3_bind_zeroblob64 sqlite3_api->bind_zeroblob64 -/* Version 3.9.0 and later */ -#define sqlite3_value_subtype sqlite3_api->value_subtype -#define sqlite3_result_subtype sqlite3_api->result_subtype -/* Version 3.10.0 and later */ -#define sqlite3_status64 sqlite3_api->status64 -#define sqlite3_strlike sqlite3_api->strlike -#define sqlite3_db_cacheflush sqlite3_api->db_cacheflush -/* Version 3.12.0 and later */ -#define sqlite3_system_errno sqlite3_api->system_errno -/* Version 3.14.0 and later */ -#define sqlite3_trace_v2 sqlite3_api->trace_v2 -#define sqlite3_expanded_sql sqlite3_api->expanded_sql -/* Version 3.18.0 and later */ -#define sqlite3_set_last_insert_rowid sqlite3_api->set_last_insert_rowid -/* Version 3.20.0 and later */ -#define sqlite3_prepare_v3 sqlite3_api->prepare_v3 -#define sqlite3_prepare16_v3 sqlite3_api->prepare16_v3 -#define sqlite3_bind_pointer sqlite3_api->bind_pointer -#define sqlite3_result_pointer sqlite3_api->result_pointer -#define sqlite3_value_pointer sqlite3_api->value_pointer -/* Version 3.22.0 and later */ -#define sqlite3_vtab_nochange sqlite3_api->vtab_nochange -#define sqlite3_value_nochange sqlite3_api->value_nochange -#define sqlite3_vtab_collation sqlite3_api->vtab_collation -/* Version 3.24.0 and later */ -#define sqlite3_keyword_count sqlite3_api->keyword_count -#define sqlite3_keyword_name sqlite3_api->keyword_name -#define sqlite3_keyword_check sqlite3_api->keyword_check -#define sqlite3_str_new sqlite3_api->str_new -#define sqlite3_str_finish sqlite3_api->str_finish -#define sqlite3_str_appendf sqlite3_api->str_appendf -#define sqlite3_str_vappendf sqlite3_api->str_vappendf -#define sqlite3_str_append sqlite3_api->str_append -#define sqlite3_str_appendall sqlite3_api->str_appendall -#define sqlite3_str_appendchar sqlite3_api->str_appendchar -#define sqlite3_str_reset sqlite3_api->str_reset -#define sqlite3_str_errcode sqlite3_api->str_errcode -#define sqlite3_str_length sqlite3_api->str_length -#define sqlite3_str_value sqlite3_api->str_value -/* Version 3.25.0 and later */ -#define sqlite3_create_window_function sqlite3_api->create_window_function -/* Version 3.26.0 and later */ -#define sqlite3_normalized_sql sqlite3_api->normalized_sql -/* Version 3.28.0 and later */ -#define sqlite3_stmt_isexplain sqlite3_api->stmt_isexplain -#define sqlite3_value_frombind sqlite3_api->value_frombind -/* Version 3.30.0 and later */ -#define sqlite3_drop_modules sqlite3_api->drop_modules -/* Version 3.31.0 and later */ -#define sqlite3_hard_heap_limit64 sqlite3_api->hard_heap_limit64 -#define sqlite3_uri_key sqlite3_api->uri_key -#define sqlite3_filename_database sqlite3_api->filename_database -#define sqlite3_filename_journal sqlite3_api->filename_journal -#define sqlite3_filename_wal sqlite3_api->filename_wal -/* Version 3.32.0 and later */ -#define sqlite3_create_filename sqlite3_api->create_filename -#define sqlite3_free_filename sqlite3_api->free_filename -#define sqlite3_database_file_object sqlite3_api->database_file_object -#endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */ - -#if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) - /* This case when the file really is being compiled as a loadable - ** extension */ -# define SQLITE_EXTENSION_INIT1 const sqlite3_api_routines *sqlite3_api=0; -# define SQLITE_EXTENSION_INIT2(v) sqlite3_api=v; -# define SQLITE_EXTENSION_INIT3 \ - extern const sqlite3_api_routines *sqlite3_api; -#else - /* This case when the file is being statically linked into the - ** application */ -# define SQLITE_EXTENSION_INIT1 /*no-op*/ -# define SQLITE_EXTENSION_INIT2(v) (void)v; /* unused parameter */ -# define SQLITE_EXTENSION_INIT3 /*no-op*/ -#endif - -#endif /* SQLITE3EXT_H */ diff --git a/src/contrib/sqlite/sqlite3pp.cpp b/src/contrib/sqlite/sqlite3pp.cpp deleted file mode 100644 index 19f9017..0000000 --- a/src/contrib/sqlite/sqlite3pp.cpp +++ /dev/null @@ -1,620 +0,0 @@ -// sqlite3pp.cpp -// -// The MIT License -// -// Copyright (c) 2015 Wongoo Lee (iwongu at gmail dot com) -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -#include -#include - -#include "sqlite3pp.h" - -namespace sqlite3pp -{ - - null_type ignore; - - namespace - { - int busy_handler_impl(void* p, int cnt) - { - auto h = static_cast(p); - return (*h)(cnt); - } - - int commit_hook_impl(void* p) - { - auto h = static_cast(p); - return (*h)(); - } - - void rollback_hook_impl(void* p) - { - auto h = static_cast(p); - (*h)(); - } - - void update_hook_impl(void* p, int opcode, char const* dbname, char const* tablename, long long int rowid) - { - auto h = static_cast(p); - (*h)(opcode, dbname, tablename, rowid); - } - - int authorizer_impl(void* p, int evcode, char const* p1, char const* p2, char const* dbname, char const* tvname) - { - auto h = static_cast(p); - return (*h)(evcode, p1, p2, dbname, tvname); - } - - } // namespace - - database::database(char const* dbname, int flags, char const* vfs) : db_(nullptr), borrowing_(false) - { - if (dbname) { - auto rc = connect(dbname, flags, vfs); - if (rc != SQLITE_OK) - throw database_error("can't connect database"); - } - } - - database::database(sqlite3* pdb) : db_(pdb), borrowing_(true) - { - } - - database::database(database&& db) : db_(std::move(db.db_)), - borrowing_(std::move(db.borrowing_)), - bh_(std::move(db.bh_)), - ch_(std::move(db.ch_)), - rh_(std::move(db.rh_)), - uh_(std::move(db.uh_)), - ah_(std::move(db.ah_)) - { - db.db_ = nullptr; - } - - database& database::operator=(database&& db) - { - db_ = std::move(db.db_); - db.db_ = nullptr; - borrowing_ = std::move(db.borrowing_); - bh_ = std::move(db.bh_); - ch_ = std::move(db.ch_); - rh_ = std::move(db.rh_); - uh_ = std::move(db.uh_); - ah_ = std::move(db.ah_); - - return *this; - } - - database::~database() - { - if (!borrowing_) { - disconnect(); - } - } - - int database::connect(char const* dbname, int flags, char const* vfs) - { - if (!borrowing_) { - disconnect(); - } - - return sqlite3_open_v2(dbname, &db_, flags, vfs); - } - - int database::disconnect() - { - auto rc = SQLITE_OK; - if (db_) { - rc = sqlite3_close(db_); - if (rc == SQLITE_OK) { - db_ = nullptr; - } - } - - return rc; - } - - int database::attach(char const* dbname, char const* name) - { - return executef("ATTACH '%q' AS '%q'", dbname, name); - } - - int database::detach(char const* name) - { - return executef("DETACH '%q'", name); - } - - int database::backup(database& destdb, backup_handler h) - { - return backup("main", destdb, "main", h); - } - - int database::backup(char const* dbname, database& destdb, char const* destdbname, backup_handler h, int step_page) - { - sqlite3_backup* bkup = sqlite3_backup_init(destdb.db_, destdbname, db_, dbname); - if (!bkup) { - return error_code(); - } - auto rc = SQLITE_OK; - do { - rc = sqlite3_backup_step(bkup, step_page); - if (h) { - h(sqlite3_backup_remaining(bkup), sqlite3_backup_pagecount(bkup), rc); - } - } while (rc == SQLITE_OK || rc == SQLITE_BUSY || rc == SQLITE_LOCKED); - sqlite3_backup_finish(bkup); - return rc; - } - - void database::set_busy_handler(busy_handler h) - { - bh_ = h; - sqlite3_busy_handler(db_, bh_ ? busy_handler_impl : 0, &bh_); - } - - void database::set_commit_handler(commit_handler h) - { - ch_ = h; - sqlite3_commit_hook(db_, ch_ ? commit_hook_impl : 0, &ch_); - } - - void database::set_rollback_handler(rollback_handler h) - { - rh_ = h; - sqlite3_rollback_hook(db_, rh_ ? rollback_hook_impl : 0, &rh_); - } - - void database::set_update_handler(update_handler h) - { - uh_ = h; - sqlite3_update_hook(db_, uh_ ? update_hook_impl : 0, &uh_); - } - - void database::set_authorize_handler(authorize_handler h) - { - ah_ = h; - sqlite3_set_authorizer(db_, ah_ ? authorizer_impl : 0, &ah_); - } - - long long int database::last_insert_rowid() const - { - return sqlite3_last_insert_rowid(db_); - } - - int database::enable_foreign_keys(bool enable) - { - return sqlite3_db_config(db_, SQLITE_DBCONFIG_ENABLE_FKEY, enable ? 1 : 0, nullptr); - } - - int database::enable_triggers(bool enable) - { - return sqlite3_db_config(db_, SQLITE_DBCONFIG_ENABLE_TRIGGER, enable ? 1 : 0, nullptr); - } - - int database::enable_extended_result_codes(bool enable) - { - return sqlite3_extended_result_codes(db_, enable ? 1 : 0); - } - - int database::changes() const - { - return sqlite3_changes(db_); - } - - int database::error_code() const - { - return sqlite3_errcode(db_); - } - - int database::extended_error_code() const - { - return sqlite3_extended_errcode(db_); - } - - char const* database::error_msg() const - { - return sqlite3_errmsg(db_); - } - - int database::execute(char const* sql) - { - return sqlite3_exec(db_, sql, 0, 0, 0); - } - - int database::executef(char const* sql, ...) - { - va_list ap; - va_start(ap, sql); - std::shared_ptr msql(sqlite3_vmprintf(sql, ap), sqlite3_free); - va_end(ap); - - return execute(msql.get()); - } - - int database::set_busy_timeout(int ms) - { - return sqlite3_busy_timeout(db_, ms); - } - - - statement::statement(database& db, char const* stmt) : db_(db), stmt_(0), tail_(0) - { - if (stmt) { - auto rc = prepare(stmt); - if (rc != SQLITE_OK) - throw database_error(db_); - } - } - - statement::~statement() - { - // finish() can return error. If you want to check the error, call - // finish() explicitly before this object is destructed. - finish(); - } - - int statement::prepare(char const* stmt) - { - auto rc = finish(); - if (rc != SQLITE_OK) - return rc; - - return prepare_impl(stmt); - } - - int statement::prepare_impl(char const* stmt) - { - return sqlite3_prepare_v2(db_.db_, stmt, std::strlen(stmt), &stmt_, &tail_); - } - - int statement::finish() - { - auto rc = SQLITE_OK; - if (stmt_) { - rc = finish_impl(stmt_); - stmt_ = nullptr; - } - tail_ = nullptr; - - return rc; - } - - int statement::finish_impl(sqlite3_stmt* stmt) - { - return sqlite3_finalize(stmt); - } - - int statement::step() - { - return sqlite3_step(stmt_); - } - - int statement::reset() - { - return sqlite3_reset(stmt_); - } - - int statement::bind(int idx, int value) - { - return sqlite3_bind_int(stmt_, idx, value); - } - - int statement::bind(int idx, double value) - { - return sqlite3_bind_double(stmt_, idx, value); - } - - int statement::bind(int idx, long long int value) - { - return sqlite3_bind_int64(stmt_, idx, value); - } - - int statement::bind(int idx, char const* value, copy_semantic fcopy) - { - return sqlite3_bind_text(stmt_, idx, value, std::strlen(value), fcopy == copy ? SQLITE_TRANSIENT : SQLITE_STATIC ); - } - - int statement::bind(int idx, void const* value, int n, copy_semantic fcopy) - { - return sqlite3_bind_blob(stmt_, idx, value, n, fcopy == copy ? SQLITE_TRANSIENT : SQLITE_STATIC ); - } - - int statement::bind(int idx, std::string const& value, copy_semantic fcopy) - { - return sqlite3_bind_text(stmt_, idx, value.c_str(), value.size(), fcopy == copy ? SQLITE_TRANSIENT : SQLITE_STATIC ); - } - - int statement::bind(int idx) - { - return sqlite3_bind_null(stmt_, idx); - } - - int statement::bind(int idx, null_type) - { - return bind(idx); - } - - int statement::bind(char const* name, int value) - { - auto idx = sqlite3_bind_parameter_index(stmt_, name); - return bind(idx, value); - } - - int statement::bind(char const* name, double value) - { - auto idx = sqlite3_bind_parameter_index(stmt_, name); - return bind(idx, value); - } - - int statement::bind(char const* name, long long int value) - { - auto idx = sqlite3_bind_parameter_index(stmt_, name); - return bind(idx, value); - } - - int statement::bind(char const* name, char const* value, copy_semantic fcopy) - { - auto idx = sqlite3_bind_parameter_index(stmt_, name); - return bind(idx, value, fcopy); - } - - int statement::bind(char const* name, void const* value, int n, copy_semantic fcopy) - { - auto idx = sqlite3_bind_parameter_index(stmt_, name); - return bind(idx, value, n, fcopy); - } - - int statement::bind(char const* name, std::string const& value, copy_semantic fcopy) - { - auto idx = sqlite3_bind_parameter_index(stmt_, name); - return bind(idx, value, fcopy); - } - - int statement::bind(char const* name) - { - auto idx = sqlite3_bind_parameter_index(stmt_, name); - return bind(idx); - } - - int statement::bind(char const* name, null_type) - { - return bind(name); - } - - - command::bindstream::bindstream(command& cmd, int idx) : cmd_(cmd), idx_(idx) - { - } - - command::command(database& db, char const* stmt) : statement(db, stmt) - { - } - - command::bindstream command::binder(int idx) - { - return bindstream(*this, idx); - } - - int command::execute() - { - auto rc = step(); - if (rc == SQLITE_DONE) rc = SQLITE_OK; - - return rc; - } - - int command::execute_all() - { - auto rc = execute(); - if (rc != SQLITE_OK) return rc; - - char const* sql = tail_; - - while (std::strlen(sql) > 0) { // sqlite3_complete() is broken. - sqlite3_stmt* old_stmt = stmt_; - - if ((rc = prepare_impl(sql)) != SQLITE_OK) return rc; - - if ((rc = sqlite3_transfer_bindings(old_stmt, stmt_)) != SQLITE_OK) return rc; - - finish_impl(old_stmt); - - if ((rc = execute()) != SQLITE_OK) return rc; - - sql = tail_; - } - - return rc; - } - - - query::rows::getstream::getstream(rows* rws, int idx) : rws_(rws), idx_(idx) - { - } - - query::rows::rows(sqlite3_stmt* stmt) : stmt_(stmt) - { - } - - int query::rows::data_count() const - { - return sqlite3_data_count(stmt_); - } - - int query::rows::column_type(int idx) const - { - return sqlite3_column_type(stmt_, idx); - } - - int query::rows::column_bytes(int idx) const - { - return sqlite3_column_bytes(stmt_, idx); - } - - int query::rows::get(int idx, int) const - { - return sqlite3_column_int(stmt_, idx); - } - - double query::rows::get(int idx, double) const - { - return sqlite3_column_double(stmt_, idx); - } - - long long int query::rows::get(int idx, long long int) const - { - return sqlite3_column_int64(stmt_, idx); - } - - char const* query::rows::get(int idx, char const*) const - { - return reinterpret_cast(sqlite3_column_text(stmt_, idx)); - } - - std::string query::rows::get(int idx, std::string) const - { - return get(idx, (char const*)0); - } - - void const* query::rows::get(int idx, void const*) const - { - return sqlite3_column_blob(stmt_, idx); - } - - null_type query::rows::get(int /*idx*/, null_type) const - { - return ignore; - } - query::rows::getstream query::rows::getter(int idx) - { - return getstream(this, idx); - } - - query::query_iterator::query_iterator() : cmd_(0) - { - rc_ = SQLITE_DONE; - } - - query::query_iterator::query_iterator(query* cmd) : cmd_(cmd) - { - rc_ = cmd_->step(); - if (rc_ != SQLITE_ROW && rc_ != SQLITE_DONE) - throw database_error(cmd_->db_); - } - - bool query::query_iterator::operator==(query::query_iterator const& other) const - { - return rc_ == other.rc_; - } - - bool query::query_iterator::operator!=(query::query_iterator const& other) const - { - return rc_ != other.rc_; - } - - query::query_iterator& query::query_iterator::operator++() - { - rc_ = cmd_->step(); - if (rc_ != SQLITE_ROW && rc_ != SQLITE_DONE) - throw database_error(cmd_->db_); - return *this; - } - - query::query_iterator::value_type query::query_iterator::operator*() const - { - return rows(cmd_->stmt_); - } - - query::query(database& db, char const* stmt) : statement(db, stmt) - { - } - - int query::column_count() const - { - return sqlite3_column_count(stmt_); - } - - char const* query::column_name(int idx) const - { - return sqlite3_column_name(stmt_, idx); - } - - char const* query::column_decltype(int idx) const - { - return sqlite3_column_decltype(stmt_, idx); - } - - - query::iterator query::begin() - { - return query_iterator(this); - } - - query::iterator query::end() - { - return query_iterator(); - } - - - transaction::transaction(database& db, bool fcommit, bool freserve) : db_(&db), fcommit_(fcommit) - { - int rc = db_->execute(freserve ? "BEGIN IMMEDIATE" : "BEGIN"); - if (rc != SQLITE_OK) - throw database_error(*db_); - } - - transaction::~transaction() - { - if (db_) { - // execute() can return error. If you want to check the error, - // call commit() or rollback() explicitly before this object is - // destructed. - db_->execute(fcommit_ ? "COMMIT" : "ROLLBACK"); - } - } - - int transaction::commit() - { - auto db = db_; - db_ = nullptr; - int rc = db->execute("COMMIT"); - return rc; - } - - int transaction::rollback() - { - auto db = db_; - db_ = nullptr; - int rc = db->execute("ROLLBACK"); - return rc; - } - - - database_error::database_error(char const* msg) : std::runtime_error(msg) - { - } - - database_error::database_error(database& db) : std::runtime_error(sqlite3_errmsg(db.db_)) - { - } - -} // namespace sqlite3pp diff --git a/src/contrib/sqlite/sqlite3pp.h b/src/contrib/sqlite/sqlite3pp.h deleted file mode 100644 index 8522f46..0000000 --- a/src/contrib/sqlite/sqlite3pp.h +++ /dev/null @@ -1,345 +0,0 @@ -// sqlite3pp.h -// -// The MIT License -// -// Copyright (c) 2015 Wongoo Lee (iwongu at gmail dot com) -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -#ifndef SQLITE3PP_H -#define SQLITE3PP_H - -#define SQLITE3PP_VERSION "1.0.8" -#define SQLITE3PP_VERSION_MAJOR 1 -#define SQLITE3PP_VERSION_MINOR 0 -#define SQLITE3PP_VERSION_PATCH 8 - -#include -#include -#include -#include -#include - -#ifdef SQLITE3PP_LOADABLE_EXTENSION -#include -SQLITE_EXTENSION_INIT1 -#else -# include "sqlite3.h" -#endif - -namespace sqlite3pp -{ - class database; - - namespace ext - { - class function; - class aggregate; - database borrow(sqlite3* pdb); - } - - template - struct convert { - using to_int = int; - }; - - class null_type {}; - extern null_type ignore; - - class noncopyable - { - protected: - noncopyable() = default; - ~noncopyable() = default; - - noncopyable(noncopyable&&) = default; - noncopyable& operator=(noncopyable&&) = default; - - noncopyable(noncopyable const&) = delete; - noncopyable& operator=(noncopyable const&) = delete; - }; - - class database : noncopyable - { - friend class statement; - friend class database_error; - friend class ext::function; - friend class ext::aggregate; - friend database ext::borrow(sqlite3* pdb); - - public: - using busy_handler = std::function; - using commit_handler = std::function; - using rollback_handler = std::function; - using update_handler = std::function; - using authorize_handler = std::function; - using backup_handler = std::function; - - explicit database(char const* dbname = nullptr, int flags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, const char* vfs = nullptr); - - database(database&& db); - database& operator=(database&& db); - - ~database(); - - int connect(char const* dbname, int flags, const char* vfs = nullptr); - int disconnect(); - - int attach(char const* dbname, char const* name); - int detach(char const* name); - - int backup(database& destdb, backup_handler h = {}); - int backup(char const* dbname, database& destdb, char const* destdbname, backup_handler h, int step_page = 5); - - long long int last_insert_rowid() const; - - int enable_foreign_keys(bool enable = true); - int enable_triggers(bool enable = true); - int enable_extended_result_codes(bool enable = true); - - int changes() const; - - int error_code() const; - int extended_error_code() const; - char const* error_msg() const; - - int execute(char const* sql); - int executef(char const* sql, ...); - - int set_busy_timeout(int ms); - - void set_busy_handler(busy_handler h); - void set_commit_handler(commit_handler h); - void set_rollback_handler(rollback_handler h); - void set_update_handler(update_handler h); - void set_authorize_handler(authorize_handler h); - - private: - database(sqlite3* pdb); - - private: - sqlite3* db_; - bool borrowing_; - - busy_handler bh_; - commit_handler ch_; - rollback_handler rh_; - update_handler uh_; - authorize_handler ah_; - }; - - class database_error : public std::runtime_error - { - public: - explicit database_error(char const* msg); - explicit database_error(database& db); - }; - - enum copy_semantic { copy, nocopy }; - - class statement : noncopyable - { - public: - int prepare(char const* stmt); - int finish(); - - int bind(int idx, int value); - int bind(int idx, double value); - int bind(int idx, long long int value); - int bind(int idx, char const* value, copy_semantic fcopy); - int bind(int idx, void const* value, int n, copy_semantic fcopy); - int bind(int idx, std::string const& value, copy_semantic fcopy); - int bind(int idx); - int bind(int idx, null_type); - - int bind(char const* name, int value); - int bind(char const* name, double value); - int bind(char const* name, long long int value); - int bind(char const* name, char const* value, copy_semantic fcopy); - int bind(char const* name, void const* value, int n, copy_semantic fcopy); - int bind(char const* name, std::string const& value, copy_semantic fcopy); - int bind(char const* name); - int bind(char const* name, null_type); - - int step(); - int reset(); - - protected: - explicit statement(database& db, char const* stmt = nullptr); - ~statement(); - - int prepare_impl(char const* stmt); - int finish_impl(sqlite3_stmt* stmt); - - protected: - database& db_; - sqlite3_stmt* stmt_; - char const* tail_; - }; - - class command : public statement - { - public: - class bindstream - { - public: - bindstream(command& cmd, int idx); - - template - bindstream& operator << (T value) { - auto rc = cmd_.bind(idx_, value); - if (rc != SQLITE_OK) { - throw database_error(cmd_.db_); - } - ++idx_; - return *this; - } - bindstream& operator << (char const* value) { - auto rc = cmd_.bind(idx_, value, copy); - if (rc != SQLITE_OK) { - throw database_error(cmd_.db_); - } - ++idx_; - return *this; - } - bindstream& operator << (std::string const& value) { - auto rc = cmd_.bind(idx_, value, copy); - if (rc != SQLITE_OK) { - throw database_error(cmd_.db_); - } - ++idx_; - return *this; - } - - private: - command& cmd_; - int idx_; - }; - - explicit command(database& db, char const* stmt = nullptr); - - bindstream binder(int idx = 1); - - int execute(); - int execute_all(); - }; - - class query : public statement - { - public: - class rows - { - public: - class getstream - { - public: - getstream(rows* rws, int idx); - - template - getstream& operator >> (T& value) { - value = rws_->get(idx_, T()); - ++idx_; - return *this; - } - - private: - rows* rws_; - int idx_; - }; - - explicit rows(sqlite3_stmt* stmt); - - int data_count() const; - int column_type(int idx) const; - - int column_bytes(int idx) const; - - template T get(int idx) const { - return get(idx, T()); - } - - template - std::tuple get_columns(typename convert::to_int... idxs) const { - return std::make_tuple(get(idxs, Ts())...); - } - - getstream getter(int idx = 0); - - private: - int get(int idx, int) const; - double get(int idx, double) const; - long long int get(int idx, long long int) const; - char const* get(int idx, char const*) const; - std::string get(int idx, std::string) const; - void const* get(int idx, void const*) const; - null_type get(int idx, null_type) const; - - private: - sqlite3_stmt* stmt_; - }; - - class query_iterator - : public std::iterator - { - public: - query_iterator(); - explicit query_iterator(query* cmd); - - bool operator==(query_iterator const&) const; - bool operator!=(query_iterator const&) const; - - query_iterator& operator++(); - - value_type operator*() const; - - private: - query* cmd_; - int rc_; - }; - - explicit query(database& db, char const* stmt = nullptr); - - int column_count() const; - - char const* column_name(int idx) const; - char const* column_decltype(int idx) const; - - using iterator = query_iterator; - - iterator begin(); - iterator end(); - }; - - class transaction : noncopyable - { - public: - explicit transaction(database& db, bool fcommit = false, bool freserve = false); - ~transaction(); - - int commit(); - int rollback(); - - private: - database* db_; - bool fcommit_; - }; - -} // namespace sqlite3pp - -#endif diff --git a/src/contrib/sqlite/sqlite3ppext.cpp b/src/contrib/sqlite/sqlite3ppext.cpp deleted file mode 100644 index 841295c..0000000 --- a/src/contrib/sqlite/sqlite3ppext.cpp +++ /dev/null @@ -1,200 +0,0 @@ -// sqlite3ppext.cpp -// -// The MIT License -// -// Copyright (c) 2015 Wongoo Lee (iwongu at gmail dot com) -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -#include - -#include "sqlite3ppext.h" - -namespace sqlite3pp -{ - namespace ext - { - - namespace - { - - void function_impl(sqlite3_context* ctx, int nargs, sqlite3_value** values) - { - auto f = static_cast(sqlite3_user_data(ctx)); - context c(ctx, nargs, values); - (*f)(c); - } - - void step_impl(sqlite3_context* ctx, int nargs, sqlite3_value** values) - { - auto p = static_cast*>(sqlite3_user_data(ctx)); - auto s = static_cast((*p).first.get()); - context c(ctx, nargs, values); - ((function::function_handler&)*s)(c); - } - - void finalize_impl(sqlite3_context* ctx) - { - auto p = static_cast*>(sqlite3_user_data(ctx)); - auto f = static_cast((*p).second.get()); - context c(ctx); - ((function::function_handler&)*f)(c); - } - - } // namespace - - database borrow(sqlite3* pdb) { - return database(pdb); - } - - context::context(sqlite3_context* ctx, int nargs, sqlite3_value** values) - : ctx_(ctx), nargs_(nargs), values_(values) - { - } - - int context::args_count() const - { - return nargs_; - } - - int context::args_bytes(int idx) const - { - return sqlite3_value_bytes(values_[idx]); - } - - int context::args_type(int idx) const - { - return sqlite3_value_type(values_[idx]); - } - - int context::get(int idx, int) const - { - return sqlite3_value_int(values_[idx]); - } - - double context::get(int idx, double) const - { - return sqlite3_value_double(values_[idx]); - } - - long long int context::get(int idx, long long int) const - { - return sqlite3_value_int64(values_[idx]); - } - - char const* context::get(int idx, char const*) const - { - return reinterpret_cast(sqlite3_value_text(values_[idx])); - } - - std::string context::get(int idx, std::string) const - { - return get(idx, (char const*)0); - } - - void const* context::get(int idx, void const*) const - { - return sqlite3_value_blob(values_[idx]); - } - - - - void context::result(int value) - { - sqlite3_result_int(ctx_, value); - } - - void context::result(double value) - { - sqlite3_result_double(ctx_, value); - } - - void context::result(long long int value) - { - sqlite3_result_int64(ctx_, value); - } - - void context::result(std::string const& value) - { - result(value.c_str(), false); - } - - void context::result(char const* value, bool fcopy) - { - sqlite3_result_text(ctx_, value, std::strlen(value), fcopy ? SQLITE_TRANSIENT : SQLITE_STATIC); - } - - void context::result(void const* value, int n, bool fcopy) - { - sqlite3_result_blob(ctx_, value, n, fcopy ? SQLITE_TRANSIENT : SQLITE_STATIC ); - } - - void context::result() - { - sqlite3_result_null(ctx_); - } - - void context::result(null_type) - { - sqlite3_result_null(ctx_); - } - - void context::result_copy(int idx) - { - sqlite3_result_value(ctx_, values_[idx]); - } - - void context::result_error(char const* msg) - { - sqlite3_result_error(ctx_, msg, std::strlen(msg)); - } - - void* context::aggregate_data(int size) - { - return sqlite3_aggregate_context(ctx_, size); - } - - int context::aggregate_count() - { - return sqlite3_aggregate_count(ctx_); - } - - function::function(database& db) : db_(db.db_) - { - } - - int function::create(char const* name, function_handler h, int nargs) - { - fh_[name] = pfunction_base(new function_handler(h)); - return sqlite3_create_function(db_, name, nargs, SQLITE_UTF8, fh_[name].get(), function_impl, 0, 0); - } - - aggregate::aggregate(database& db) : db_(db.db_) - { - } - - int aggregate::create(char const* name, function_handler s, function_handler f, int nargs) - { - ah_[name] = std::make_pair(pfunction_base(new function_handler(s)), pfunction_base(new function_handler(f))); - return sqlite3_create_function(db_, name, nargs, SQLITE_UTF8, &ah_[name], 0, step_impl, finalize_impl); - } - - } // namespace ext - -} // namespace sqlite3pp diff --git a/src/contrib/sqlite/sqlite3ppext.h b/src/contrib/sqlite/sqlite3ppext.h deleted file mode 100644 index ed79398..0000000 --- a/src/contrib/sqlite/sqlite3ppext.h +++ /dev/null @@ -1,232 +0,0 @@ -// sqlite3ppext.h -// -// The MIT License -// -// Copyright (c) 2015 Wongoo Lee (iwongu at gmail dot com) -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -#ifndef SQLITE3PPEXT_H -#define SQLITE3PPEXT_H - -#include -#include -#include -#include -#include -#include - -#include "sqlite3pp.h" - -namespace sqlite3pp -{ - namespace - { - template - struct Apply { - template - static inline auto apply(F&& f, T&& t, A&&... a) - -> decltype(Apply::apply(std::forward(f), - std::forward(t), - std::get(std::forward(t)), - std::forward(a)...)) - { - return Apply::apply(std::forward(f), - std::forward(t), - std::get(std::forward(t)), - std::forward(a)...); - } - }; - - template<> - struct Apply<0> { - template - static inline auto apply(F&& f, T&&, A&&... a) - -> decltype(std::forward(f)(std::forward(a)...)) - { - return std::forward(f)(std::forward(a)...); - } - }; - - template - inline auto apply(F&& f, T&& t) - -> decltype(Apply::type>::value>::apply(std::forward(f), std::forward(t))) - { - return Apply::type>::value>::apply( - std::forward(f), std::forward(t)); - } - } - - - namespace ext - { - database borrow(sqlite3* pdb); - - class context : noncopyable - { - public: - explicit context(sqlite3_context* ctx, int nargs = 0, sqlite3_value** values = nullptr); - - int args_count() const; - int args_bytes(int idx) const; - int args_type(int idx) const; - - template T get(int idx) const { - return get(idx, T()); - } - - void result(int value); - void result(double value); - void result(long long int value); - void result(std::string const& value); - void result(char const* value, bool fcopy); - void result(void const* value, int n, bool fcopy); - void result(); - void result(null_type); - void result_copy(int idx); - void result_error(char const* msg); - - void* aggregate_data(int size); - int aggregate_count(); - - template - std::tuple to_tuple() { - return to_tuple_impl(0, *this, std::tuple()); - } - - private: - int get(int idx, int) const; - double get(int idx, double) const; - long long int get(int idx, long long int) const; - char const* get(int idx, char const*) const; - std::string get(int idx, std::string) const; - void const* get(int idx, void const*) const; - - template - static inline std::tuple to_tuple_impl(int index, const context& c, std::tuple&&) - { - auto h = std::make_tuple(c.context::get(index)); - return std::tuple_cat(h, to_tuple_impl(++index, c, std::tuple())); - } - static inline std::tuple<> to_tuple_impl(int /*index*/, const context& /*c*/, std::tuple<>&&) - { - return std::tuple<>(); - } - - private: - sqlite3_context* ctx_; - int nargs_; - sqlite3_value** values_; - }; - - namespace - { - template - void functionx_impl(sqlite3_context* ctx, int nargs, sqlite3_value** values) - { - context c(ctx, nargs, values); - auto f = static_cast*>(sqlite3_user_data(ctx)); - c.result(apply(*f, c.to_tuple())); - } - } - - class function : noncopyable - { - public: - using function_handler = std::function; - using pfunction_base = std::shared_ptr; - - explicit function(database& db); - - int create(char const* name, function_handler h, int nargs = 0); - - template int create(char const* name, std::function h) { - fh_[name] = std::shared_ptr(new std::function(h)); - return create_function_impl()(db_, fh_[name].get(), name); - } - - private: - - template - struct create_function_impl; - - template - struct create_function_impl - { - int operator()(sqlite3* db, void* fh, char const* name) { - return sqlite3_create_function(db, name, sizeof...(Ps), SQLITE_UTF8, fh, - functionx_impl, - 0, 0); - } - }; - - private: - sqlite3* db_; - - std::map fh_; - }; - - namespace - { - template - void stepx_impl(sqlite3_context* ctx, int nargs, sqlite3_value** values) - { - context c(ctx, nargs, values); - T* t = static_cast(c.aggregate_data(sizeof(T))); - if (c.aggregate_count() == 1) new (t) T; - apply([](T* tt, Ps... ps){tt->step(ps...);}, - std::tuple_cat(std::make_tuple(t), c.to_tuple())); - } - - template - void finishN_impl(sqlite3_context* ctx) - { - context c(ctx); - T* t = static_cast(c.aggregate_data(sizeof(T))); - c.result(t->finish()); - t->~T(); - } - } - - class aggregate : noncopyable - { - public: - using function_handler = std::function; - using pfunction_base = std::shared_ptr; - - explicit aggregate(database& db); - - int create(char const* name, function_handler s, function_handler f, int nargs = 1); - - template - int create(char const* name) { - return sqlite3_create_function(db_, name, sizeof...(Ps), SQLITE_UTF8, 0, 0, stepx_impl, finishN_impl); - } - - private: - sqlite3* db_; - - std::map > ah_; - }; - - } // namespace ext - -} // namespace sqlite3pp - -#endif diff --git a/src/contrib/sqlite/sqlite_orm.h b/src/contrib/sqlite/sqlite_orm.h new file mode 100644 index 0000000..e69a73a --- /dev/null +++ b/src/contrib/sqlite/sqlite_orm.h @@ -0,0 +1,13348 @@ +#pragma once + +#if defined(_MSC_VER) +#if defined(min) +__pragma(push_macro("min")) +#undef min +#define __RESTORE_MIN__ +#endif +#if defined(max) + __pragma(push_macro("max")) +#undef max +#define __RESTORE_MAX__ +#endif +#endif // defined(_MSC_VER) + +#include // due to #166 + +#if __cplusplus >= 201703L // use of C++17 or higher +// Enables use of std::optional in SQLITE_ORM. +#define SQLITE_ORM_OPTIONAL_SUPPORTED +#endif +#pragma once + +#include // std::error_code, std::system_error +#include // std::string +#include "sqlite3.h" +#include +#include // std::ostringstream + + namespace sqlite_orm { + + enum class orm_error_code { + not_found = 1, + type_is_not_mapped_to_storage, + trying_to_dereference_null_iterator, + too_many_tables_specified, + incorrect_set_fields_specified, + column_not_found, + table_has_no_primary_key_column, + cannot_start_a_transaction_within_a_transaction, + no_active_transaction, + incorrect_journal_mode_string, + invalid_collate_argument_enum, + failed_to_init_a_backup, + unknown_member_value, + incorrect_order, + }; +} + +namespace sqlite_orm { + + class orm_error_category : public std::error_category { + public: + const char *name() const noexcept override final { + return "ORM error"; + } + + std::string message(int c) const override final { + switch(static_cast(c)) { + case orm_error_code::not_found: + return "Not found"; + case orm_error_code::type_is_not_mapped_to_storage: + return "Type is not mapped to storage"; + case orm_error_code::trying_to_dereference_null_iterator: + return "Trying to dereference null iterator"; + case orm_error_code::too_many_tables_specified: + return "Too many tables specified"; + case orm_error_code::incorrect_set_fields_specified: + return "Incorrect set fields specified"; + case orm_error_code::column_not_found: + return "Column not found"; + case orm_error_code::table_has_no_primary_key_column: + return "Table has no primary key column"; + case orm_error_code::cannot_start_a_transaction_within_a_transaction: + return "Cannot start a transaction within a transaction"; + case orm_error_code::no_active_transaction: + return "No active transaction"; + case orm_error_code::invalid_collate_argument_enum: + return "Invalid collate_argument enum"; + case orm_error_code::failed_to_init_a_backup: + return "Failed to init a backup"; + case orm_error_code::unknown_member_value: + return "Unknown member value"; + case orm_error_code::incorrect_order: + return "Incorrect order"; + default: + return "unknown error"; + } + } + }; + + class sqlite_error_category : public std::error_category { + public: + const char *name() const noexcept override final { + return "SQLite error"; + } + + std::string message(int c) const override final { + return sqlite3_errstr(c); + } + }; + + inline const orm_error_category &get_orm_error_category() { + static orm_error_category res; + return res; + } + + inline const sqlite_error_category &get_sqlite_error_category() { + static sqlite_error_category res; + return res; + } + + template + std::string get_error_message(sqlite3 *db, T &&... args) { + std::ostringstream stream; + using unpack = int[]; + static_cast(unpack{0, (static_cast(static_cast(stream << args)), 0)...}); + stream << sqlite3_errmsg(db); + return stream.str(); + } + + template + [[noreturn]] void throw_error(sqlite3 *db, T &&... args) { + throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), + get_error_message(db, std::forward(args)...)); + } +} + +namespace std { + template<> + struct is_error_code_enum : std::true_type {}; + + inline std::error_code make_error_code(sqlite_orm::orm_error_code errorCode) { + return std::error_code(static_cast(errorCode), sqlite_orm::get_orm_error_category()); + } +} +#pragma once + +#include // std::tuple, std::get +#include // std::false_type, std::true_type + +// #include "static_magic.h" + +#include // std::false_type, std::true_type, std::integral_constant + +namespace sqlite_orm { + + // got from here + // https://stackoverflow.com/questions/37617677/implementing-a-compile-time-static-if-logic-for-different-string-types-in-a-co + namespace internal { + + static inline decltype(auto) empty_callable() { + static auto res = [](auto &&...) {}; + return (res); + } + + template + decltype(auto) static_if(std::true_type, const T &t, const F &) { + return (t); + } + + template + decltype(auto) static_if(std::false_type, const T &, const F &f) { + return (f); + } + + template + decltype(auto) static_if(const T &t, const F &f) { + return static_if(std::integral_constant{}, t, f); + } + + template + decltype(auto) static_if(const T &t) { + return static_if(std::integral_constant{}, t, empty_callable()); + } + + template + using static_not = std::integral_constant; + } + +} + +namespace sqlite_orm { + + // got from here http://stackoverflow.com/questions/25958259/how-do-i-find-out-if-a-tuple-contains-a-type + namespace tuple_helper { + + template + struct has_type; + + template + struct has_type> : std::false_type {}; + + template + struct has_type> : has_type> {}; + + template + struct has_type> : std::true_type {}; + + template + using tuple_contains_type = typename has_type::type; + + template + struct iterator { + + template + void operator()(const std::tuple &t, const L &l, bool reverse = true) { + if(reverse) { + l(std::get(t)); + iterator()(t, l, reverse); + } else { + iterator()(t, l, reverse); + l(std::get(t)); + } + } + }; + + template + struct iterator<0, Args...> { + + template + void operator()(const std::tuple &t, const L &l, bool /*reverse*/ = true) { + l(std::get<0>(t)); + } + }; + + template + struct iterator { + + template + void operator()(const std::tuple<> &, const L &, bool /*reverse*/ = true) { + //.. + } + }; + + template + void move_tuple_impl(L &lhs, R &rhs) { + std::get(lhs) = std::move(std::get(rhs)); + internal::static_if{}>([](auto &lhs, auto &rhs) { + move_tuple_impl(lhs, rhs); + })(lhs, rhs); + } + } + + namespace internal { + + template + void move_tuple(L &lhs, R &rhs) { + using bool_type = std::integral_constant; + static_if([](auto &lhs, auto &rhs) { + tuple_helper::move_tuple_impl(lhs, rhs); + })(lhs, rhs); + } + + template + void iterate_tuple(const std::tuple &t, const L &l) { + using tuple_type = std::tuple; + tuple_helper::iterator::value - 1, Args...>()(t, l, false); + } + + template + using tuple_cat_t = decltype(std::tuple_cat(std::declval()...)); + + template + struct conc_tuple { + using type = tuple_cat_t; + }; + + template class C> + struct count_tuple; + + template class C> + struct count_tuple, C> { + static constexpr const int value = 0; + }; + + template class C> + struct count_tuple, C> { + static constexpr const int value = C::value + count_tuple, C>::value; + }; + } +} +#pragma once + +#include // std::string +#include // std::shared_ptr, std::unique_ptr +#include // std::vector +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED +#include // std::optional +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED + +namespace sqlite_orm { + + /** + * This class accepts c++ type and transfers it to sqlite name (int -> INTEGER, std::string -> TEXT) + */ + template + struct type_printer; + + struct integer_printer { + inline const std::string &print() { + static const std::string res = "INTEGER"; + return res; + } + }; + + struct text_printer { + inline const std::string &print() { + static const std::string res = "TEXT"; + return res; + } + }; + + struct real_printer { + inline const std::string &print() { + static const std::string res = "REAL"; + return res; + } + }; + + struct blob_printer { + inline const std::string &print() { + static const std::string res = "BLOB"; + return res; + } + }; + + // Note unsigned/signed char and simple char used for storing integer values, not char values. + template<> + struct type_printer : public integer_printer {}; + + template<> + struct type_printer : public integer_printer {}; + + template<> + struct type_printer : public integer_printer {}; + + template<> + struct type_printer : public integer_printer {}; + + template<> + struct type_printer : public integer_printer {}; + + template<> + struct type_printer : public integer_printer {}; + + template<> + struct type_printer : public integer_printer {}; + + template<> + struct type_printer : public integer_printer {}; + + template<> + struct type_printer : public integer_printer {}; + + template<> + struct type_printer : public integer_printer {}; + + template<> + struct type_printer : public integer_printer {}; + + template<> + struct type_printer : public integer_printer {}; + + template<> + struct type_printer : public text_printer {}; + + template<> + struct type_printer : public text_printer {}; + + template<> + struct type_printer : public text_printer {}; + + template<> + struct type_printer : public real_printer {}; + + template<> + struct type_printer : public real_printer {}; + + template + struct type_printer, void> : public type_printer {}; + + template + struct type_printer, void> : public type_printer {}; + +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + template + struct type_printer, void> : public type_printer {}; +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED + + template<> + struct type_printer, void> : public blob_printer {}; +} +#pragma once + +namespace sqlite_orm { + + namespace internal { + + enum class collate_argument { + binary, + nocase, + rtrim, + }; + } + +} +#pragma once + +#include // std::string +#include // std::tuple, std::make_tuple +#include // std::stringstream +#include // std::is_base_of, std::false_type, std::true_type +#include // std::ostream + +namespace sqlite_orm { + + namespace constraints { + + /** + * AUTOINCREMENT constraint class. + */ + struct autoincrement_t { + + operator std::string() const { + return "AUTOINCREMENT"; + } + }; + + struct primary_key_base { + enum class order_by { + unspecified, + ascending, + descending, + }; + + order_by asc_option = order_by::unspecified; + + operator std::string() const { + std::string res = "PRIMARY KEY"; + switch(this->asc_option) { + case order_by::ascending: + res += " ASC"; + break; + case order_by::descending: + res += " DESC"; + break; + default: + break; + } + return res; + } + }; + + /** + * PRIMARY KEY constraint class. + * Cs is parameter pack which contains columns (member pointers and/or function pointers). Can be empty when + * used withen `make_column` function. + */ + template + struct primary_key_t : primary_key_base { + using order_by = primary_key_base::order_by; + using columns_tuple = std::tuple; + + columns_tuple columns; + + primary_key_t(decltype(columns) c) : columns(move(c)) {} + + primary_key_t asc() const { + auto res = *this; + res.asc_option = order_by::ascending; + return res; + } + + primary_key_t desc() const { + auto res = *this; + res.asc_option = order_by::descending; + return res; + } + }; + + struct unique_base { + operator std::string() const { + return "UNIQUE"; + } + }; + + /** + * UNIQUE constraint class. + */ + template + struct unique_t : unique_base { + using columns_tuple = std::tuple; + + columns_tuple columns; + + unique_t(columns_tuple columns_) : columns(move(columns_)) {} + }; + + /** + * DEFAULT constraint class. + * T is a value type. + */ + template + struct default_t { + using value_type = T; + + value_type value; + + operator std::string() const { + return "DEFAULT"; + } + }; + +#if SQLITE_VERSION_NUMBER >= 3006019 + + /** + * FOREIGN KEY constraint class. + * Cs are columns which has foreign key + * Rs are column which C references to + * Available in SQLite 3.6.19 or higher + */ + + template + struct foreign_key_t; + + enum class foreign_key_action { + none, // not specified + no_action, + restrict_, + set_null, + set_default, + cascade, + }; + + inline std::ostream &operator<<(std::ostream &os, foreign_key_action action) { + switch(action) { + case decltype(action)::no_action: + os << "NO ACTION"; + break; + case decltype(action)::restrict_: + os << "RESTRICT"; + break; + case decltype(action)::set_null: + os << "SET NULL"; + break; + case decltype(action)::set_default: + os << "SET DEFAULT"; + break; + case decltype(action)::cascade: + os << "CASCADE"; + break; + case decltype(action)::none: + break; + } + return os; + } + + struct on_update_delete_base { + const bool update; // true if update and false if delete + + operator std::string() const { + if(this->update) { + return "ON UPDATE"; + } else { + return "ON DELETE"; + } + } + }; + + /** + * F - foreign key class + */ + template + struct on_update_delete_t : on_update_delete_base { + using foreign_key_type = F; + + const foreign_key_type &fk; + + on_update_delete_t(decltype(fk) fk_, decltype(update) update, foreign_key_action action_) : + on_update_delete_base{update}, fk(fk_), _action(action_) {} + + foreign_key_action _action = foreign_key_action::none; + + foreign_key_type no_action() const { + auto res = this->fk; + if(update) { + res.on_update._action = foreign_key_action::no_action; + } else { + res.on_delete._action = foreign_key_action::no_action; + } + return res; + } + + foreign_key_type restrict_() const { + auto res = this->fk; + if(update) { + res.on_update._action = foreign_key_action::restrict_; + } else { + res.on_delete._action = foreign_key_action::restrict_; + } + return res; + } + + foreign_key_type set_null() const { + auto res = this->fk; + if(update) { + res.on_update._action = foreign_key_action::set_null; + } else { + res.on_delete._action = foreign_key_action::set_null; + } + return res; + } + + foreign_key_type set_default() const { + auto res = this->fk; + if(update) { + res.on_update._action = foreign_key_action::set_default; + } else { + res.on_delete._action = foreign_key_action::set_default; + } + return res; + } + + foreign_key_type cascade() const { + auto res = this->fk; + if(update) { + res.on_update._action = foreign_key_action::cascade; + } else { + res.on_delete._action = foreign_key_action::cascade; + } + return res; + } + + operator bool() const { + return this->_action != decltype(this->_action)::none; + } + }; + + template + struct foreign_key_t, std::tuple> { + using columns_type = std::tuple; + using references_type = std::tuple; + using self = foreign_key_t; + + columns_type columns; + references_type references; + + on_update_delete_t on_update; + on_update_delete_t on_delete; + + static_assert(std::tuple_size::value == std::tuple_size::value, + "Columns size must be equal to references tuple"); + + foreign_key_t(columns_type columns_, references_type references_) : + columns(std::move(columns_)), references(std::move(references_)), + on_update(*this, true, foreign_key_action::none), on_delete(*this, false, foreign_key_action::none) {} + + foreign_key_t(const self &other) : + columns(other.columns), references(other.references), on_update(*this, true, other.on_update._action), + on_delete(*this, false, other.on_delete._action) {} + + self &operator=(const self &other) { + this->columns = other.columns; + this->references = other.references; + this->on_update = {*this, true, other.on_update._action}; + this->on_delete = {*this, false, other.on_delete._action}; + return *this; + } + + template + void for_each_column(const L &) {} + + template + constexpr bool has_every() const { + return false; + } + }; + + /** + * Cs can be a class member pointer, a getter function member pointer or setter + * func member pointer + * Available in SQLite 3.6.19 or higher + */ + template + struct foreign_key_intermediate_t { + using tuple_type = std::tuple; + + tuple_type columns; + + foreign_key_intermediate_t(tuple_type columns_) : columns(std::move(columns_)) {} + + template + foreign_key_t, std::tuple> references(Rs... references) { + return {std::move(this->columns), std::make_tuple(std::forward(references)...)}; + } + }; +#endif + + struct collate_t { + internal::collate_argument argument = internal::collate_argument::binary; + + collate_t(internal::collate_argument argument_) : argument(argument_) {} + + operator std::string() const { + std::string res = "COLLATE " + this->string_from_collate_argument(this->argument); + return res; + } + + static std::string string_from_collate_argument(internal::collate_argument argument) { + switch(argument) { + case decltype(argument)::binary: + return "BINARY"; + case decltype(argument)::nocase: + return "NOCASE"; + case decltype(argument)::rtrim: + return "RTRIM"; + } + throw std::system_error(std::make_error_code(orm_error_code::invalid_collate_argument_enum)); + } + }; + + struct check_string { + operator std::string() const { + return "CHECK"; + } + }; + + template + struct check_t : check_string { + using expression_type = T; + + expression_type expression; + + check_t(expression_type expression_) : expression(std::move(expression_)) {} + }; + + template + struct is_constraint : std::false_type {}; + + template<> + struct is_constraint : std::true_type {}; + + template + struct is_constraint> : std::true_type {}; + + template + struct is_constraint> : std::true_type {}; + + template + struct is_constraint> : std::true_type {}; + + template + struct is_constraint> : std::true_type {}; + + template<> + struct is_constraint : std::true_type {}; + + template + struct is_constraint> : std::true_type {}; + + template + struct constraints_size; + + template<> + struct constraints_size<> { + static constexpr const int value = 0; + }; + + template + struct constraints_size { + static constexpr const int value = is_constraint::value + constraints_size::value; + }; + } + +#if SQLITE_VERSION_NUMBER >= 3006019 + + /** + * FOREIGN KEY constraint construction function that takes member pointer as argument + * Available in SQLite 3.6.19 or higher + */ + template + constraints::foreign_key_intermediate_t foreign_key(Cs... columns) { + return {std::make_tuple(std::forward(columns)...)}; + } +#endif + + /** + * UNIQUE constraint builder function. + */ + template + constraints::unique_t unique(Args... args) { + return {std::make_tuple(std::forward(args)...)}; + } + + inline constraints::unique_t<> unique() { + return {{}}; + } + + inline constraints::autoincrement_t autoincrement() { + return {}; + } + + template + constraints::primary_key_t primary_key(Cs... cs) { + return {std::make_tuple(std::forward(cs)...)}; + } + + inline constraints::primary_key_t<> primary_key() { + return {{}}; + } + + template + constraints::default_t default_value(T t) { + return {std::move(t)}; + } + + inline constraints::collate_t collate_nocase() { + return {internal::collate_argument::nocase}; + } + + inline constraints::collate_t collate_binary() { + return {internal::collate_argument::binary}; + } + + inline constraints::collate_t collate_rtrim() { + return {internal::collate_argument::rtrim}; + } + + template + constraints::check_t check(T t) { + return {std::move(t)}; + } + + namespace internal { + + /** + * FOREIGN KEY traits. Common case + */ + template + struct is_foreign_key : std::false_type {}; + + /** + * FOREIGN KEY traits. Specialized case + */ + template + struct is_foreign_key> : std::true_type {}; + + /** + * PRIMARY KEY traits. Common case + */ + template + struct is_primary_key : public std::false_type {}; + + /** + * PRIMARY KEY traits. Specialized case + */ + template + struct is_primary_key> : public std::true_type {}; + } + +} +#pragma once + +#include // std::false_type, std::true_type +#include // std::shared_ptr, std::unique_ptr +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED +#include // std::optional +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED + +namespace sqlite_orm { + + /** + * This is class that tells `sqlite_orm` that type is nullable. Nullable types + * are mapped to sqlite database as `NULL` and not-nullable are mapped as `NOT NULL`. + * Default nullability status for all types is `NOT NULL`. So if you want to map + * custom type as `NULL` (for example: boost::optional) you have to create a specialiation + * of type_is_nullable for your type and derive from `std::true_type`. + */ + template + struct type_is_nullable : public std::false_type { + bool operator()(const T &) const { + return true; + } + }; + + /** + * This is a specialization for std::shared_ptr. std::shared_ptr is nullable in sqlite_orm. + */ + template + struct type_is_nullable> : public std::true_type { + bool operator()(const std::shared_ptr &t) const { + return static_cast(t); + } + }; + + /** + * This is a specialization for std::unique_ptr. std::unique_ptr is nullable too. + */ + template + struct type_is_nullable> : public std::true_type { + bool operator()(const std::unique_ptr &t) const { + return static_cast(t); + } + }; + +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + /** + * This is a specialization for std::optional. std::optional is nullable. + */ + template + struct type_is_nullable> : public std::true_type { + bool operator()(const std::optional &t) const { + return t.has_value(); + } + }; +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED + +} +#pragma once + +#include // std::unique_ptr +#include // std::string +#include // std::stringstream + +// #include "constraints.h" + +// #include "serializator_context.h" + +namespace sqlite_orm { + + namespace internal { + + struct serializator_context_base { + bool replace_bindable_with_question = false; + bool skip_table_name = true; + bool use_parentheses = true; + + template + std::string column_name(F O::*) const { + return {}; + } + }; + + template + struct serializator_context : serializator_context_base { + using impl_type = I; + + const impl_type &impl; + + serializator_context(const impl_type &impl_) : impl(impl_) {} + + template + std::string column_name(F O::*m) const { + return this->impl.column_name(m); + } + }; + + template + struct serializator_context_builder { + using storage_type = S; + using impl_type = typename storage_type::impl_type; + + serializator_context_builder(const storage_type &storage_) : storage(storage_) {} + + serializator_context operator()() const { + return {this->storage.impl}; + } + + const storage_type &storage; + }; + + } + +} + +namespace sqlite_orm { + + namespace internal { + + template + std::string serialize(const T &t); + + /** + * This class is used in tuple interation to know whether tuple constains `default_value_t` + * constraint class and what it's value if it is + */ + struct default_value_extractor { + + template + std::unique_ptr operator()(const A &) { + return {}; + } + + template + std::unique_ptr operator()(const constraints::default_t &t) { + serializator_context_base context; + return std::make_unique(serialize(t.value, context)); + } + }; + + } + +} +#pragma once + +#include // std::false_type, std::true_type + +// #include "negatable.h" + +namespace sqlite_orm { + namespace internal { + struct negatable_t {}; + } +} + +namespace sqlite_orm { + + namespace internal { + + /** + * Inherit this class to support arithmetic types overloading + */ + struct arithmetic_t {}; + + template + struct binary_operator : Ds... { + using left_type = L; + using right_type = R; + + left_type lhs; + right_type rhs; + + binary_operator(left_type lhs_, right_type rhs_) : lhs(std::move(lhs_)), rhs(std::move(rhs_)) {} + }; + + struct conc_string { + operator std::string() const { + return "||"; + } + }; + + /** + * Result of concatenation || operator + */ + template + using conc_t = binary_operator; + + struct add_string { + operator std::string() const { + return "+"; + } + }; + + /** + * Result of addition + operator + */ + template + using add_t = binary_operator; + + struct sub_string { + operator std::string() const { + return "-"; + } + }; + + /** + * Result of substitute - operator + */ + template + using sub_t = binary_operator; + + struct mul_string { + operator std::string() const { + return "*"; + } + }; + + /** + * Result of multiply * operator + */ + template + using mul_t = binary_operator; + + struct div_string { + operator std::string() const { + return "/"; + } + }; + + /** + * Result of divide / operator + */ + template + using div_t = binary_operator; + + struct mod_string { + operator std::string() const { + return "%"; + } + }; + + /** + * Result of mod % operator + */ + template + using mod_t = binary_operator; + + struct bitwise_shift_left_string { + operator std::string() const { + return "<<"; + } + }; + + /** + * Result of bitwise shift left << operator + */ + template + using bitwise_shift_left_t = binary_operator; + + struct bitwise_shift_right_string { + operator std::string() const { + return ">>"; + } + }; + + /** + * Result of bitwise shift right >> operator + */ + template + using bitwise_shift_right_t = binary_operator; + + struct bitwise_and_string { + operator std::string() const { + return "&"; + } + }; + + /** + * Result of bitwise and & operator + */ + template + using bitwise_and_t = binary_operator; + + struct bitwise_or_string { + operator std::string() const { + return "|"; + } + }; + + /** + * Result of bitwise or | operator + */ + template + using bitwise_or_t = binary_operator; + + struct bitwise_not_string { + operator std::string() const { + return "~"; + } + }; + + /** + * Result of bitwise not ~ operator + */ + template + struct bitwise_not_t : bitwise_not_string, arithmetic_t, negatable_t { + using argument_type = T; + + argument_type argument; + + bitwise_not_t(argument_type argument_) : argument(std::move(argument_)) {} + }; + + struct assign_string { + operator std::string() const { + return "="; + } + }; + /** + * Result of assign = operator + */ + template + using assign_t = binary_operator; + + /** + * Assign operator traits. Common case + */ + template + struct is_assign_t : public std::false_type {}; + + /** + * Assign operator traits. Specialized case + */ + template + struct is_assign_t> : public std::true_type {}; + + /** + * Is not an operator but a result of c(...) function. Has operator= overloaded which returns assign_t + */ + template + struct expression_t { + T t; + + expression_t(T t_) : t(std::move(t_)) {} + + template + assign_t operator=(R r) const { + return {this->t, std::move(r)}; + } + + assign_t operator=(std::nullptr_t) const { + return {this->t, nullptr}; + } + }; + + } + + /** + * Public interface for syntax sugar for columns. Example: `where(c(&User::id) == 5)` or + * `storage.update(set(c(&User::name) = "Dua Lipa")); + */ + template + internal::expression_t c(T t) { + return {std::move(t)}; + } + + /** + * Public interface for || concatenation operator. Example: `select(conc(&User::name, "@gmail.com"));` => SELECT + * name || '@gmail.com' FROM users + */ + template + internal::conc_t conc(L l, R r) { + return {std::move(l), std::move(r)}; + } + + /** + * Public interface for + operator. Example: `select(add(&User::age, 100));` => SELECT age + 100 FROM users + */ + template + internal::add_t add(L l, R r) { + return {std::move(l), std::move(r)}; + } + + /** + * Public interface for - operator. Example: `select(add(&User::age, 1));` => SELECT age - 1 FROM users + */ + template + internal::sub_t sub(L l, R r) { + return {std::move(l), std::move(r)}; + } + + template + internal::mul_t mul(L l, R r) { + return {std::move(l), std::move(r)}; + } + + template + internal::div_t div(L l, R r) { + return {std::move(l), std::move(r)}; + } + + template + internal::mod_t mod(L l, R r) { + return {std::move(l), std::move(r)}; + } + + template + internal::bitwise_shift_left_t bitwise_shift_left(L l, R r) { + return {std::move(l), std::move(r)}; + } + + template + internal::bitwise_shift_right_t bitwise_shift_right(L l, R r) { + return {std::move(l), std::move(r)}; + } + + template + internal::bitwise_and_t bitwise_and(L l, R r) { + return {std::move(l), std::move(r)}; + } + + template + internal::bitwise_or_t bitwise_or(L l, R r) { + return {std::move(l), std::move(r)}; + } + + template + internal::bitwise_not_t bitwise_not(T t) { + return {std::move(t)}; + } + + template + internal::assign_t assign(L l, R r) { + return {std::move(l), std::move(r)}; + } + +} +#pragma once + +#include // std::tuple +#include // std::string +#include // std::unique_ptr +#include // std::true_type, std::false_type, std::is_same, std::enable_if, std::is_member_pointer, std::is_member_function_pointer + +// #include "type_is_nullable.h" + +// #include "tuple_helper.h" + +// #include "default_value_extractor.h" + +// #include "constraints.h" + +// #include "getter_traits.h" + +namespace sqlite_orm { + + namespace internal { + + template + struct is_field_member_pointer : std::false_type {}; + + template + struct is_field_member_pointer::value && + !std::is_member_function_pointer::value>::type> + : std::true_type {}; + + template + struct field_member_traits; + + template + struct field_member_traits::value>::type> { + using object_type = O; + using field_type = F; + }; + + /** + * Getters aliases + */ + template + using getter_by_value_const = T (O::*)() const; + + template + using getter_by_value = T (O::*)(); + + template + using getter_by_ref_const = T &(O::*)() const; + + template + using getter_by_ref = T &(O::*)(); + + template + using getter_by_const_ref_const = const T &(O::*)() const; + + template + using getter_by_const_ref = const T &(O::*)(); + + /** + * Setters aliases + */ + template + using setter_by_value = void (O::*)(T); + + template + using setter_by_ref = void (O::*)(T &); + + template + using setter_by_const_ref = void (O::*)(const T &); + + template + struct is_getter : std::false_type {}; + + template + struct is_getter> : std::true_type {}; + + template + struct is_getter> : std::true_type {}; + + template + struct is_getter> : std::true_type {}; + + template + struct is_getter> : std::true_type {}; + + template + struct is_getter> : std::true_type {}; + + template + struct is_getter> : std::true_type {}; + + template + struct is_setter : std::false_type {}; + + template + struct is_setter> : std::true_type {}; + + template + struct is_setter> : std::true_type {}; + + template + struct is_setter> : std::true_type {}; + + template + struct getter_traits; + + template + struct getter_traits> { + using object_type = O; + using field_type = T; + + static constexpr const bool returns_lvalue = false; + }; + + template + struct getter_traits> { + using object_type = O; + using field_type = T; + + static constexpr const bool returns_lvalue = false; + }; + + template + struct getter_traits> { + using object_type = O; + using field_type = T; + + static constexpr const bool returns_lvalue = true; + }; + + template + struct getter_traits> { + using object_type = O; + using field_type = T; + + static constexpr const bool returns_lvalue = true; + }; + + template + struct getter_traits> { + using object_type = O; + using field_type = T; + + static constexpr const bool returns_lvalue = true; + }; + + template + struct getter_traits> { + using object_type = O; + using field_type = T; + + static constexpr const bool returns_lvalue = true; + }; + + template + struct setter_traits; + + template + struct setter_traits> { + using object_type = O; + using field_type = T; + }; + + template + struct setter_traits> { + using object_type = O; + using field_type = T; + }; + + template + struct setter_traits> { + using object_type = O; + using field_type = T; + }; + + template + struct member_traits; + + template + struct member_traits::value>::type> { + using object_type = typename field_member_traits::object_type; + using field_type = typename field_member_traits::field_type; + }; + + template + struct member_traits::value>::type> { + using object_type = typename getter_traits::object_type; + using field_type = typename getter_traits::field_type; + }; + + template + struct member_traits::value>::type> { + using object_type = typename setter_traits::object_type; + using field_type = typename setter_traits::field_type; + }; + } +} + +namespace sqlite_orm { + + namespace internal { + + struct column_base { + + /** + * Column name. Specified during construction in `make_column`. + */ + const std::string name; + }; + + /** + * This class stores single column info. column_t is a pair of [column_name:member_pointer] mapped to a storage + * O is a mapped class, e.g. User + * T is a mapped class'es field type, e.g. &User::name + * Op... is a constraints pack, e.g. primary_key_t, autoincrement_t etc + */ + template + struct column_t : column_base { + using object_type = O; + using field_type = T; + using constraints_type = std::tuple; + using member_pointer_t = field_type object_type::*; + using getter_type = G; + using setter_type = S; + + /** + * Member pointer used to read/write member + */ + member_pointer_t member_pointer /* = nullptr*/; + + /** + * Getter member function pointer to get a value. If member_pointer is null than + * `getter` and `setter` must be not null + */ + getter_type getter /* = nullptr*/; + + /** + * Setter member function + */ + setter_type setter /* = nullptr*/; + + /** + * Constraints tuple + */ + constraints_type constraints; + + column_t(std::string name, + member_pointer_t member_pointer_, + getter_type getter_, + setter_type setter_, + constraints_type constraints_) : + column_base{std::move(name)}, + member_pointer(member_pointer_), getter(getter_), setter(setter_), constraints(move(constraints_)) {} + + /** + * Simplified interface for `NOT NULL` constraint + */ + bool not_null() const { + return !type_is_nullable::value; + } + + template + constexpr bool has() const { + return tuple_helper::tuple_contains_type::value; + } + + template + constexpr bool has_every() const { + if(has() && has()) { + return true; + } else { + return has_every(); + } + } + + template + constexpr bool has_every() const { + return has(); + } + + /** + * Simplified interface for `DEFAULT` constraint + * @return string representation of default value if it exists otherwise nullptr + */ + std::unique_ptr default_value() const { + std::unique_ptr res; + iterate_tuple(this->constraints, [&res](auto &v) { + auto dft = internal::default_value_extractor()(v); + if(dft) { + res = std::move(dft); + } + }); + return res; + } + }; + + /** + * Column traits. Common case. + */ + template + struct is_column : public std::false_type {}; + + /** + * Column traits. Specialized case case. + */ + template + struct is_column> : public std::true_type {}; + + template + struct column_field_type { + using type = void; + }; + + template + struct column_field_type> { + using type = typename column_t::field_type; + }; + + template + struct column_constraints_type { + using type = std::tuple<>; + }; + + template + struct column_constraints_type> { + using type = typename column_t::constraints_type; + }; + + } + + /** + * Column builder function. You should use it to create columns instead of constructor + */ + template::value>::type, + class... Op> + internal::column_t + make_column(const std::string &name, T O::*m, Op... constraints) { + static_assert(constraints::template constraints_size::value == std::tuple_size>::value, + "Incorrect constraints pack"); + static_assert(internal::is_field_member_pointer::value, + "second argument expected as a member field pointer, not member function pointer"); + return {name, m, nullptr, nullptr, std::make_tuple(constraints...)}; + } + + /** + * Column builder function with setter and getter. You should use it to create columns instead of constructor + */ + template::value>::type, + typename = typename std::enable_if::value>::type, + class... Op> + internal::column_t::object_type, + typename internal::setter_traits::field_type, + G, + S, + Op...> + make_column(const std::string &name, S setter, G getter, Op... constraints) { + static_assert(std::is_same::field_type, + typename internal::getter_traits::field_type>::value, + "Getter and setter must get and set same data type"); + static_assert(constraints::template constraints_size::value == std::tuple_size>::value, + "Incorrect constraints pack"); + return {name, nullptr, getter, setter, std::make_tuple(constraints...)}; + } + + /** + * Column builder function with getter and setter (reverse order). You should use it to create columns instead of + * constructor + */ + template::value>::type, + typename = typename std::enable_if::value>::type, + class... Op> + internal::column_t::object_type, + typename internal::setter_traits::field_type, + G, + S, + Op...> + make_column(const std::string &name, G getter, S setter, Op... constraints) { + static_assert(std::is_same::field_type, + typename internal::getter_traits::field_type>::value, + "Getter and setter must get and set same data type"); + static_assert(constraints::template constraints_size::value == std::tuple_size>::value, + "Incorrect constraints pack"); + return {name, nullptr, getter, setter, std::make_tuple(constraints...)}; + } + +} +#pragma once + +#include // std::string +#include // std::stringstream +#include // std::vector +#include // std::nullptr_t +#include // std::shared_ptr, std::unique_ptr +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED +#include // std::optional +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED + +namespace sqlite_orm { + + /** + * Is used to print members mapped to objects in storage_t::dump member function. + * Other developers can create own specialization to map custom types + */ + template + struct field_printer { + std::string operator()(const T &t) const { + std::stringstream stream; + stream << t; + return stream.str(); + } + }; + + /** + * Upgrade to integer is required when using unsigned char(uint8_t) + */ + template<> + struct field_printer { + std::string operator()(const unsigned char &t) const { + std::stringstream stream; + stream << +t; + return stream.str(); + } + }; + + /** + * Upgrade to integer is required when using signed char(int8_t) + */ + template<> + struct field_printer { + std::string operator()(const signed char &t) const { + std::stringstream stream; + stream << +t; + return stream.str(); + } + }; + + /** + * char is neigher signer char nor unsigned char so it has its own specialization + */ + template<> + struct field_printer { + std::string operator()(const char &t) const { + std::stringstream stream; + stream << +t; + return stream.str(); + } + }; + + template<> + struct field_printer { + std::string operator()(const std::string &t) const { + return t; + } + }; + + template<> + struct field_printer> { + std::string operator()(const std::vector &t) const { + std::stringstream ss; + ss << std::hex; + for(auto c: t) { + ss << c; + } + return ss.str(); + } + }; + + template<> + struct field_printer { + std::string operator()(const std::nullptr_t &) const { + return "null"; + } + }; + + template + struct field_printer> { + std::string operator()(const std::shared_ptr &t) const { + if(t) { + return field_printer()(*t); + } else { + return field_printer()(nullptr); + } + } + }; + + template + struct field_printer> { + std::string operator()(const std::unique_ptr &t) const { + if(t) { + return field_printer()(*t); + } else { + return field_printer()(nullptr); + } + } + }; + +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + template + struct field_printer> { + std::string operator()(const std::optional &t) const { + if(t.has_value()) { + return field_printer()(*t); + } else { + return field_printer()(nullptr); + } + } + }; +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED +} +#pragma once + +#include // std::string +#include // std::enable_if, std::is_same +#include // std::vector +#include // std::tuple + +// #include "collate_argument.h" + +// #include "constraints.h" + +// #include "optional_container.h" + +namespace sqlite_orm { + + namespace internal { + + /** + * This is a cute class which allows storing something or nothing + * depending on template argument. Useful for optional class members + */ + template + struct optional_container { + using type = T; + + type field; + + template + void apply(const L &l) const { + l(this->field); + } + }; + + template<> + struct optional_container { + using type = void; + + template + void apply(const L &) const { + //.. + } + }; + } +} + +// #include "negatable.h" + +namespace sqlite_orm { + + namespace internal { + struct arithmetic_t; + } + + namespace internal { + + struct limit_string { + operator std::string() const { + return "LIMIT"; + } + }; + + /** + * Stores LIMIT/OFFSET info + */ + template + struct limit_t : limit_string { + T lim; + internal::optional_container off; + + limit_t() = default; + + limit_t(decltype(lim) lim_) : lim(std::move(lim_)) {} + + limit_t(decltype(lim) lim_, decltype(off) off_) : lim(std::move(lim_)), off(std::move(off_)) {} + }; + + template + struct is_limit : std::false_type {}; + + template + struct is_limit> : std::true_type {}; + + /** + * Stores OFFSET only info + */ + template + struct offset_t { + T off; + }; + + template + struct is_offset : std::false_type {}; + + template + struct is_offset> : std::true_type {}; + + /** + * Inherit from this class if target class can be chained with other conditions with '&&' and '||' operators + */ + struct condition_t {}; + + /** + * Collated something + */ + template + struct collate_t : public condition_t { + T expr; + internal::collate_argument argument; + + collate_t(T expr_, internal::collate_argument argument_) : expr(expr_), argument(argument_) {} + + operator std::string() const { + return constraints::collate_t{this->argument}; + } + }; + + struct named_collate_base { + std::string name; + + operator std::string() const { + return "COLLATE " + this->name; + } + }; + + /** + * Collated something with custom collate function + */ + template + struct named_collate : named_collate_base { + T expr; + + named_collate(T expr_, std::string name_) : named_collate_base{std::move(name_)}, expr(std::move(expr_)) {} + }; + + struct negated_condition_string { + operator std::string() const { + return "NOT"; + } + }; + + /** + * Result of not operator + */ + template + struct negated_condition_t : condition_t, negated_condition_string { + C c; + + negated_condition_t(C c_) : c(std::move(c_)) {} + }; + + /** + * Base class for binary conditions + */ + template + struct binary_condition : public condition_t { + using left_type = L; + using right_type = R; + + left_type l; + right_type r; + + binary_condition() = default; + + binary_condition(left_type l_, right_type r_) : l(std::move(l_)), r(std::move(r_)) {} + }; + + struct and_condition_string { + operator std::string() const { + return "AND"; + } + }; + + /** + * Result of and operator + */ + template + struct and_condition_t : binary_condition, and_condition_string { + using super = binary_condition; + + using super::super; + }; + + struct or_condition_string { + operator std::string() const { + return "OR"; + } + }; + + /** + * Result of or operator + */ + template + struct or_condition_t : binary_condition, or_condition_string { + using super = binary_condition; + + using super::super; + }; + + struct is_equal_string { + operator std::string() const { + return "="; + } + }; + + /** + * = and == operators object + */ + template + struct is_equal_t : binary_condition, is_equal_string, internal::negatable_t { + using self = is_equal_t; + + using binary_condition::binary_condition; + + collate_t collate_binary() const { + return {*this, internal::collate_argument::binary}; + } + + collate_t collate_nocase() const { + return {*this, internal::collate_argument::nocase}; + } + + collate_t collate_rtrim() const { + return {*this, internal::collate_argument::rtrim}; + } + + named_collate collate(std::string name) const { + return {*this, std::move(name)}; + } + }; + + struct is_not_equal_string { + operator std::string() const { + return "!="; + } + }; + + /** + * != operator object + */ + template + struct is_not_equal_t : binary_condition, is_not_equal_string, internal::negatable_t { + using self = is_not_equal_t; + + using binary_condition::binary_condition; + + collate_t collate_binary() const { + return {*this, internal::collate_argument::binary}; + } + + collate_t collate_nocase() const { + return {*this, internal::collate_argument::nocase}; + } + + collate_t collate_rtrim() const { + return {*this, internal::collate_argument::rtrim}; + } + }; + + struct greater_than_string { + operator std::string() const { + return ">"; + } + }; + + /** + * > operator object. + */ + template + struct greater_than_t : binary_condition, greater_than_string, internal::negatable_t { + using self = greater_than_t; + + using binary_condition::binary_condition; + + collate_t collate_binary() const { + return {*this, internal::collate_argument::binary}; + } + + collate_t collate_nocase() const { + return {*this, internal::collate_argument::nocase}; + } + + collate_t collate_rtrim() const { + return {*this, internal::collate_argument::rtrim}; + } + }; + + struct greater_or_equal_string { + operator std::string() const { + return ">="; + } + }; + + /** + * >= operator object. + */ + template + struct greater_or_equal_t : binary_condition, greater_or_equal_string, internal::negatable_t { + using self = greater_or_equal_t; + + using binary_condition::binary_condition; + + collate_t collate_binary() const { + return {*this, internal::collate_argument::binary}; + } + + collate_t collate_nocase() const { + return {*this, internal::collate_argument::nocase}; + } + + collate_t collate_rtrim() const { + return {*this, internal::collate_argument::rtrim}; + } + }; + + struct lesser_than_string { + operator std::string() const { + return "<"; + } + }; + + /** + * < operator object. + */ + template + struct lesser_than_t : binary_condition, lesser_than_string, internal::negatable_t { + using self = lesser_than_t; + + using binary_condition::binary_condition; + + collate_t collate_binary() const { + return {*this, internal::collate_argument::binary}; + } + + collate_t collate_nocase() const { + return {*this, internal::collate_argument::nocase}; + } + + collate_t collate_rtrim() const { + return {*this, internal::collate_argument::rtrim}; + } + }; + + struct lesser_or_equal_string { + operator std::string() const { + return "<="; + } + }; + + /** + * <= operator object. + */ + template + struct lesser_or_equal_t : binary_condition, lesser_or_equal_string, internal::negatable_t { + using self = lesser_or_equal_t; + + using binary_condition::binary_condition; + + collate_t collate_binary() const { + return {*this, internal::collate_argument::binary}; + } + + collate_t collate_nocase() const { + return {*this, internal::collate_argument::nocase}; + } + + collate_t collate_rtrim() const { + return {*this, internal::collate_argument::rtrim}; + } + }; + + struct in_base { + bool negative = false; // used in not_in + + operator std::string() const { + if(!this->negative) { + return "IN"; + } else { + return "NOT IN"; + } + } + }; + + /** + * IN operator object. + */ + template + struct in_t : condition_t, in_base, internal::negatable_t { + using self = in_t; + + L l; // left expression + A arg; // in arg + + in_t(L l_, A arg_, bool negative) : in_base{negative}, l(l_), arg(std::move(arg_)) {} + }; + + struct is_null_string { + operator std::string() const { + return "IS NULL"; + } + }; + + /** + * IS NULL operator object. + */ + template + struct is_null_t : is_null_string, internal::negatable_t { + using self = is_null_t; + + T t; + + is_null_t(T t_) : t(std::move(t_)) {} + }; + + struct is_not_null_string { + operator std::string() const { + return "IS NOT NULL"; + } + }; + + /** + * IS NOT NULL operator object. + */ + template + struct is_not_null_t : is_not_null_string, internal::negatable_t { + using self = is_not_null_t; + + T t; + + is_not_null_t(T t_) : t(std::move(t_)) {} + }; + + struct where_string { + operator std::string() const { + return "WHERE"; + } + }; + + /** + * WHERE argument holder. + * C is conditions type. Can be any condition like: is_equal_t, is_null_t, exists_t etc + */ + template + struct where_t : where_string { + C c; + + where_t(C c_) : c(std::move(c_)) {} + }; + + template + struct is_where : std::false_type {}; + + template + struct is_where> : std::true_type {}; + + struct order_by_base { + int asc_desc = 0; // 1: asc, -1: desc + std::string _collate_argument; + }; + + struct order_by_string { + operator std::string() const { + return "ORDER BY"; + } + }; + + /** + * ORDER BY argument holder. + */ + template + struct order_by_t : order_by_base, order_by_string { + using self = order_by_t; + + O o; + + order_by_t(O o_) : o(std::move(o_)) {} + + self asc() { + auto res = *this; + res.asc_desc = 1; + return res; + } + + self desc() { + auto res = *this; + res.asc_desc = -1; + return res; + } + + self collate_binary() const { + auto res = *this; + res._collate_argument = constraints::collate_t::string_from_collate_argument( + sqlite_orm::internal::collate_argument::binary); + return res; + } + + self collate_nocase() const { + auto res = *this; + res._collate_argument = constraints::collate_t::string_from_collate_argument( + sqlite_orm::internal::collate_argument::nocase); + return res; + } + + self collate_rtrim() const { + auto res = *this; + res._collate_argument = + constraints::collate_t::string_from_collate_argument(sqlite_orm::internal::collate_argument::rtrim); + return res; + } + + self collate(std::string name) const { + auto res = *this; + res._collate_argument = std::move(name); + return res; + } + }; + + /** + * ORDER BY pack holder. + */ + template + struct multi_order_by_t : order_by_string { + using args_type = std::tuple; + + args_type args; + + multi_order_by_t(args_type &&args_) : args(std::move(args_)) {} + }; + + struct dynamic_order_by_entry_t : order_by_base { + std::string name; + + dynamic_order_by_entry_t(decltype(name) name_, int asc_desc, std::string collate_argument) : + order_by_base{asc_desc, move(collate_argument)}, name(move(name_)) {} + }; + + /** + * C - serializator context class + */ + template + struct dynamic_order_by_t : order_by_string { + using context_t = C; + using entry_t = dynamic_order_by_entry_t; + using const_iterator = typename std::vector::const_iterator; + + dynamic_order_by_t(const context_t &context_) : context(context_) {} + + template + void push_back(order_by_t order_by) { + auto newContext = this->context; + newContext.skip_table_name = true; + auto columnName = serialize(order_by.o, newContext); + entries.emplace_back(move(columnName), order_by.asc_desc, move(order_by._collate_argument)); + } + + const_iterator begin() const { + return this->entries.begin(); + } + + const_iterator end() const { + return this->entries.end(); + } + + void clear() { + this->entries.clear(); + } + + protected: + std::vector entries; + context_t context; + }; + + template + struct is_order_by : std::false_type {}; + + template + struct is_order_by> : std::true_type {}; + + template + struct is_order_by> : std::true_type {}; + + template + struct is_order_by> : std::true_type {}; + + struct group_by_string { + operator std::string() const { + return "GROUP BY"; + } + }; + + /** + * GROUP BY pack holder. + */ + template + struct group_by_t : group_by_string { + using args_type = std::tuple; + args_type args; + + group_by_t(args_type &&args_) : args(std::move(args_)) {} + }; + + template + struct is_group_by : std::false_type {}; + + template + struct is_group_by> : std::true_type {}; + + struct between_string { + operator std::string() const { + return "BETWEEN"; + } + }; + + /** + * BETWEEN operator object. + */ + template + struct between_t : condition_t, between_string { + using expression_type = A; + using lower_type = T; + using upper_type = T; + + expression_type expr; + lower_type b1; + upper_type b2; + + between_t(expression_type expr_, lower_type b1_, upper_type b2_) : + expr(std::move(expr_)), b1(std::move(b1_)), b2(std::move(b2_)) {} + }; + + struct like_string { + operator std::string() const { + return "LIKE"; + } + }; + + /** + * LIKE operator object. + */ + template + struct like_t : condition_t, like_string, internal::negatable_t { + using self = like_t; + using arg_t = A; + using pattern_t = T; + using escape_t = E; + + arg_t arg; + pattern_t pattern; + sqlite_orm::internal::optional_container + arg3; // not escape cause escape exists as a function here + + like_t(arg_t arg_, pattern_t pattern_, sqlite_orm::internal::optional_container escape) : + arg(std::move(arg_)), pattern(std::move(pattern_)), arg3(std::move(escape)) {} + + template + like_t escape(C c) const { + sqlite_orm::internal::optional_container newArg3{std::move(c)}; + return {std::move(this->arg), std::move(this->pattern), std::move(newArg3)}; + } + }; + + struct glob_string { + operator std::string() const { + return "GLOB"; + } + }; + + template + struct glob_t : condition_t, glob_string, internal::negatable_t { + using self = glob_t; + using arg_t = A; + using pattern_t = T; + + arg_t arg; + pattern_t pattern; + + glob_t(arg_t arg_, pattern_t pattern_) : arg(std::move(arg_)), pattern(std::move(pattern_)) {} + }; + + struct cross_join_string { + operator std::string() const { + return "CROSS JOIN"; + } + }; + + /** + * CROSS JOIN holder. + * T is joined type which represents any mapped table. + */ + template + struct cross_join_t : cross_join_string { + using type = T; + }; + + struct natural_join_string { + operator std::string() const { + return "NATURAL JOIN"; + } + }; + + /** + * NATURAL JOIN holder. + * T is joined type which represents any mapped table. + */ + template + struct natural_join_t : natural_join_string { + using type = T; + }; + + struct left_join_string { + operator std::string() const { + return "LEFT JOIN"; + } + }; + + /** + * LEFT JOIN holder. + * T is joined type which represents any mapped table. + * O is on(...) argument type. + */ + template + struct left_join_t : left_join_string { + using type = T; + using on_type = O; + + on_type constraint; + + left_join_t(on_type constraint_) : constraint(std::move(constraint_)) {} + }; + + struct join_string { + operator std::string() const { + return "JOIN"; + } + }; + + /** + * Simple JOIN holder. + * T is joined type which represents any mapped table. + * O is on(...) argument type. + */ + template + struct join_t : join_string { + using type = T; + using on_type = O; + + on_type constraint; + + join_t(on_type constraint_) : constraint(std::move(constraint_)) {} + }; + + struct left_outer_join_string { + operator std::string() const { + return "LEFT OUTER JOIN"; + } + }; + + /** + * LEFT OUTER JOIN holder. + * T is joined type which represents any mapped table. + * O is on(...) argument type. + */ + template + struct left_outer_join_t : left_outer_join_string { + using type = T; + using on_type = O; + + on_type constraint; + + left_outer_join_t(on_type constraint_) : constraint(std::move(constraint_)) {} + }; + + struct on_string { + operator std::string() const { + return "ON"; + } + }; + + /** + * on(...) argument holder used for JOIN, LEFT JOIN, LEFT OUTER JOIN and INNER JOIN + * T is on type argument. + */ + template + struct on_t : on_string { + using arg_type = T; + + arg_type arg; + + on_t(arg_type arg_) : arg(std::move(arg_)) {} + }; + + /** + * USING argument holder. + */ + template + struct using_t { + F O::*column = nullptr; + + operator std::string() const { + return "USING"; + } + }; + + struct inner_join_string { + operator std::string() const { + return "INNER JOIN"; + } + }; + + /** + * INNER JOIN holder. + * T is joined type which represents any mapped table. + * O is on(...) argument type. + */ + template + struct inner_join_t : inner_join_string { + using type = T; + using on_type = O; + + on_type constraint; + + inner_join_t(on_type constraint_) : constraint(std::move(constraint_)) {} + }; + + struct exists_string { + operator std::string() const { + return "EXISTS"; + } + }; + + template + struct exists_t : condition_t, exists_string, internal::negatable_t { + using type = T; + using self = exists_t; + + type t; + + exists_t(T t_) : t(std::move(t_)) {} + }; + + struct having_string { + operator std::string() const { + return "HAVING"; + } + }; + + /** + * HAVING holder. + * T is having argument type. + */ + template + struct having_t : having_string { + using type = T; + + type t; + + having_t(type t_) : t(std::move(t_)) {} + }; + + template + struct is_having : std::false_type {}; + + template + struct is_having> : std::true_type {}; + + struct cast_string { + operator std::string() const { + return "CAST"; + } + }; + + /** + * CAST holder. + * T is a type to cast to + * E is an expression type + * Example: cast(&User::id) + */ + template + struct cast_t : cast_string { + using to_type = T; + using expression_type = E; + + expression_type expression; + + cast_t(expression_type expression_) : expression(std::move(expression_)) {} + }; + + } + + template::value>::type> + internal::negated_condition_t operator!(T arg) { + return {std::move(arg)}; + } + + /** + * Cute operators for columns + */ + template + internal::lesser_than_t operator<(internal::expression_t expr, R r) { + return {std::move(expr.t), std::move(r)}; + } + + template + internal::lesser_than_t operator<(L l, internal::expression_t expr) { + return {std::move(l), std::move(expr.t)}; + } + + template + internal::lesser_or_equal_t operator<=(internal::expression_t expr, R r) { + return {std::move(expr.t), std::move(r)}; + } + + template + internal::lesser_or_equal_t operator<=(L l, internal::expression_t expr) { + return {std::move(l), std::move(expr.t)}; + } + + template + internal::greater_than_t operator>(internal::expression_t expr, R r) { + return {std::move(expr.t), std::move(r)}; + } + + template + internal::greater_than_t operator>(L l, internal::expression_t expr) { + return {std::move(l), std::move(expr.t)}; + } + + template + internal::greater_or_equal_t operator>=(internal::expression_t expr, R r) { + return {std::move(expr.t), std::move(r)}; + } + + template + internal::greater_or_equal_t operator>=(L l, internal::expression_t expr) { + return {std::move(l), std::move(expr.t)}; + } + + template + internal::is_equal_t operator==(internal::expression_t expr, R r) { + return {std::move(expr.t), std::move(r)}; + } + + template + internal::is_equal_t operator==(L l, internal::expression_t expr) { + return {std::move(l), std::move(expr.t)}; + } + + template + internal::is_not_equal_t operator!=(internal::expression_t expr, R r) { + return {std::move(expr.t), std::move(r)}; + } + + template + internal::is_not_equal_t operator!=(L l, internal::expression_t expr) { + return {std::move(l), std::move(expr.t)}; + } + + template + internal::conc_t operator||(internal::expression_t expr, R r) { + return {std::move(expr.t), std::move(r)}; + } + + template + internal::conc_t operator||(L l, internal::expression_t expr) { + return {std::move(l), std::move(expr.t)}; + } + + template + internal::conc_t operator||(internal::expression_t l, internal::expression_t r) { + return {std::move(l.t), std::move(r.t)}; + } + + template + internal::add_t operator+(internal::expression_t expr, R r) { + return {std::move(expr.t), std::move(r)}; + } + + template + internal::add_t operator+(L l, internal::expression_t expr) { + return {std::move(l), std::move(expr.t)}; + } + + template + internal::add_t operator+(internal::expression_t l, internal::expression_t r) { + return {std::move(l.t), std::move(r.t)}; + } + + template + internal::sub_t operator-(internal::expression_t expr, R r) { + return {std::move(expr.t), std::move(r)}; + } + + template + internal::sub_t operator-(L l, internal::expression_t expr) { + return {std::move(l), std::move(expr.t)}; + } + + template + internal::sub_t operator-(internal::expression_t l, internal::expression_t r) { + return {std::move(l.t), std::move(r.t)}; + } + + template + internal::mul_t operator*(internal::expression_t expr, R r) { + return {std::move(expr.t), std::move(r)}; + } + + template + internal::mul_t operator*(L l, internal::expression_t expr) { + return {std::move(l), std::move(expr.t)}; + } + + template + internal::mul_t operator*(internal::expression_t l, internal::expression_t r) { + return {std::move(l.t), std::move(r.t)}; + } + + template + internal::div_t operator/(internal::expression_t expr, R r) { + return {std::move(expr.t), std::move(r)}; + } + + template + internal::div_t operator/(L l, internal::expression_t expr) { + return {std::move(l), std::move(expr.t)}; + } + + template + internal::div_t operator/(internal::expression_t l, internal::expression_t r) { + return {std::move(l.t), std::move(r.t)}; + } + + template + internal::mod_t operator%(internal::expression_t expr, R r) { + return {std::move(expr.t), std::move(r)}; + } + + template + internal::mod_t operator%(L l, internal::expression_t expr) { + return {std::move(l), std::move(expr.t)}; + } + + template + internal::mod_t operator%(internal::expression_t l, internal::expression_t r) { + return {std::move(l.t), std::move(r.t)}; + } + + template + internal::using_t using_(F O::*p) { + return {std::move(p)}; + } + + template + internal::on_t on(T t) { + return {std::move(t)}; + } + + template + internal::cross_join_t cross_join() { + return {}; + } + + template + internal::natural_join_t natural_join() { + return {}; + } + + template + internal::left_join_t left_join(O o) { + return {std::move(o)}; + } + + template + internal::join_t join(O o) { + return {std::move(o)}; + } + + template + internal::left_outer_join_t left_outer_join(O o) { + return {std::move(o)}; + } + + template + internal::inner_join_t inner_join(O o) { + return {std::move(o)}; + } + + template + internal::offset_t offset(T off) { + return {std::move(off)}; + } + + template + internal::limit_t limit(T lim) { + return {std::move(lim)}; + } + + template + typename std::enable_if::value, internal::limit_t>::type limit(O off, + T lim) { + return {std::move(lim), {std::move(off)}}; + } + + template + internal::limit_t limit(T lim, internal::offset_t offt) { + return {std::move(lim), {std::move(offt.off)}}; + } + + template::value || + std::is_base_of::value>::type> + internal::and_condition_t operator&&(L l, R r) { + return {std::move(l), std::move(r)}; + } + + template::value || + std::is_base_of::value>::type> + internal::or_condition_t operator||(L l, R r) { + return {std::move(l), std::move(r)}; + } + + template + internal::is_not_null_t is_not_null(T t) { + return {std::move(t)}; + } + + template + internal::is_null_t is_null(T t) { + return {std::move(t)}; + } + + template + internal::in_t> in(L l, std::vector values) { + return {std::move(l), std::move(values), false}; + } + + template + internal::in_t> in(L l, std::initializer_list values) { + return {std::move(l), std::move(values), false}; + } + + template + internal::in_t in(L l, A arg) { + return {std::move(l), std::move(arg), false}; + } + + template + internal::in_t> not_in(L l, std::vector values) { + return {std::move(l), std::move(values), true}; + } + + template + internal::in_t> not_in(L l, std::initializer_list values) { + return {std::move(l), std::move(values), true}; + } + + template + internal::in_t not_in(L l, A arg) { + return {std::move(l), std::move(arg), true}; + } + + template + internal::is_equal_t is_equal(L l, R r) { + return {std::move(l), std::move(r)}; + } + + template + internal::is_equal_t eq(L l, R r) { + return {std::move(l), std::move(r)}; + } + + template + internal::is_not_equal_t is_not_equal(L l, R r) { + return {std::move(l), std::move(r)}; + } + + template + internal::is_not_equal_t ne(L l, R r) { + return {std::move(l), std::move(r)}; + } + + template + internal::greater_than_t greater_than(L l, R r) { + return {std::move(l), std::move(r)}; + } + + template + internal::greater_than_t gt(L l, R r) { + return {std::move(l), std::move(r)}; + } + + template + internal::greater_or_equal_t greater_or_equal(L l, R r) { + return {std::move(l), std::move(r)}; + } + + template + internal::greater_or_equal_t ge(L l, R r) { + return {std::move(l), std::move(r)}; + } + + template + internal::lesser_than_t lesser_than(L l, R r) { + return {std::move(l), std::move(r)}; + } + + template + internal::lesser_than_t lt(L l, R r) { + return {std::move(l), std::move(r)}; + } + + template + internal::lesser_or_equal_t lesser_or_equal(L l, R r) { + return {std::move(l), std::move(r)}; + } + + template + internal::lesser_or_equal_t le(L l, R r) { + return {std::move(l), std::move(r)}; + } + + template + internal::where_t where(C c) { + return {std::move(c)}; + } + + /** + * ORDER BY column + * Example: storage.select(&User::name, order_by(&User::id)) + */ + template + internal::order_by_t order_by(O o) { + return {std::move(o)}; + } + + /** + * ORDER BY column1, column2 + * Example: storage.get_all(multi_order_by(order_by(&Singer::name).asc(), order_by(&Singer::gender).desc()) + */ + template + internal::multi_order_by_t multi_order_by(Args &&... args) { + return {std::make_tuple(std::forward(args)...)}; + } + + /** + * ORDER BY column1, column2 + * Difference from `multi_order_by` is that `dynamic_order_by` can be changed at runtime using `push_back` member + * function Example: + * auto orderBy = dynamic_order_by(storage); + * if(someCondition) { + * orderBy.push_back(&User::id); + * } else { + * orderBy.push_back(&User::name); + * orderBy.push_back(&User::birthDate); + * } + */ + template + internal::dynamic_order_by_t> + dynamic_order_by(const S &storage) { + internal::serializator_context_builder builder(storage); + return builder(); + } + + /** + * GROUP BY column. + * Example: storage.get_all(group_by(&Employee::name)) + */ + template + internal::group_by_t group_by(Args &&... args) { + return {std::make_tuple(std::forward(args)...)}; + } + + /** + * X BETWEEN Y AND Z + * Example: storage.select(between(&User::id, 10, 20)) + */ + template + internal::between_t between(A expr, T b1, T b2) { + return {std::move(expr), std::move(b1), std::move(b2)}; + } + + /** + * X LIKE Y + * Example: storage.select(like(&User::name, "T%")) + */ + template + internal::like_t like(A a, T t) { + return {std::move(a), std::move(t), {}}; + } + + /** + * X GLOB Y + * Example: storage.select(glob(&User::name, "*S")) + */ + template + internal::glob_t glob(A a, T t) { + return {std::move(a), std::move(t)}; + } + + /** + * X LIKE Y ESCAPE Z + * Example: storage.select(like(&User::name, "T%", "%")) + */ + template + internal::like_t like(A a, T t, E e) { + return {std::move(a), std::move(t), {std::move(e)}}; + } + + /** + * EXISTS(condition). + * Example: storage.select(columns(&Agent::code, &Agent::name, &Agent::workingArea, &Agent::comission), + where(exists(select(asterisk(), + where(is_equal(&Customer::grade, 3) and + is_equal(&Agent::code, &Customer::agentCode))))), + order_by(&Agent::comission)); + */ + template + internal::exists_t exists(T t) { + return {std::move(t)}; + } + + /** + * HAVING(expression). + * Example: storage.get_all(group_by(&Employee::name), having(greater_than(count(&Employee::name), 2))); + */ + template + internal::having_t having(T t) { + return {std::move(t)}; + } + + /** + * CAST(X AS type). + * Example: cast(&User::id) + */ + template + internal::cast_t cast(E e) { + return {std::move(e)}; + } +} +#pragma once + +#include // std::enable_if, std::is_base_of, std::is_member_pointer +#include // std::stringstream +#include // std::string + +namespace sqlite_orm { + + /** + * This is base class for every class which is used as a custom table alias. + * For more information please look through self_join.cpp example + */ + struct alias_tag {}; + + namespace internal { + + /** + * This is a common built-in class used for custom single character table aliases. + * Also you can use language aliases `alias_a`, `alias_b` etc. instead + */ + template + struct table_alias : alias_tag { + using type = T; + + static char get() { + return A; + } + }; + + /** + * Column expression with table alias attached like 'C.ID'. This is not a column alias + */ + template + struct alias_column_t { + using alias_type = T; + using column_type = C; + + column_type column; + + alias_column_t(){}; + + alias_column_t(column_type column_) : column(column_) {} + }; + + template + struct alias_extractor; + + template + struct alias_extractor::value>::type> { + static std::string get() { + std::stringstream ss; + ss << T::get(); + return ss.str(); + } + }; + + template + struct alias_extractor::value>::type> { + static std::string get() { + return {}; + } + }; + + template + struct as_t { + using alias_type = T; + using expression_type = E; + + expression_type expression; + }; + + template + struct alias_holder { + using type = T; + }; + } + + /** + * @return column with table alias attached. Place it instead of a column statement in case you need to specify a + * column with table alias prefix like 'a.column'. For more information please look through self_join.cpp example + */ + template + internal::alias_column_t alias_column(C c) { + static_assert(std::is_member_pointer::value, + "alias_column argument must be a member pointer mapped to a storage"); + return {c}; + } + + template + internal::as_t as(E expression) { + return {std::move(expression)}; + } + + template + internal::alias_holder get() { + return {}; + } + + template + using alias_a = internal::table_alias; + template + using alias_b = internal::table_alias; + template + using alias_c = internal::table_alias; + template + using alias_d = internal::table_alias; + template + using alias_e = internal::table_alias; + template + using alias_f = internal::table_alias; + template + using alias_g = internal::table_alias; + template + using alias_h = internal::table_alias; + template + using alias_i = internal::table_alias; + template + using alias_j = internal::table_alias; + template + using alias_k = internal::table_alias; + template + using alias_l = internal::table_alias; + template + using alias_m = internal::table_alias; + template + using alias_n = internal::table_alias; + template + using alias_o = internal::table_alias; + template + using alias_p = internal::table_alias; + template + using alias_q = internal::table_alias; + template + using alias_r = internal::table_alias; + template + using alias_s = internal::table_alias; + template + using alias_t = internal::table_alias; + template + using alias_u = internal::table_alias; + template + using alias_v = internal::table_alias; + template + using alias_w = internal::table_alias; + template + using alias_x = internal::table_alias; + template + using alias_y = internal::table_alias; + template + using alias_z = internal::table_alias; +} +#pragma once + +// #include "conditions.h" + +namespace sqlite_orm { + + namespace internal { + + template + struct join_iterator { + + template + void operator()(const L &) { + //.. + } + }; + + template<> + struct join_iterator<> { + + template + void operator()(const L &) { + //.. + } + }; + + template + struct join_iterator : public join_iterator { + using super = join_iterator; + + template + void operator()(const L &l) { + this->super::operator()(l); + } + }; + + template + struct join_iterator, Tail...> : public join_iterator { + using super = join_iterator; + using join_type = cross_join_t; + + template + void operator()(const L &l) { + l(*this); + this->super::operator()(l); + } + }; + + template + struct join_iterator, Tail...> : public join_iterator { + using super = join_iterator; + using join_type = natural_join_t; + + template + void operator()(const L &l) { + l(*this); + this->super::operator()(l); + } + }; + + template + struct join_iterator, Tail...> : public join_iterator { + using super = join_iterator; + using join_type = left_join_t; + + template + void operator()(const L &l) { + l(*this); + this->super::operator()(l); + } + }; + + template + struct join_iterator, Tail...> : public join_iterator { + using super = join_iterator; + using join_type = join_t; + + template + void operator()(const L &l) { + l(*this); + this->super::operator()(l); + } + }; + + template + struct join_iterator, Tail...> : public join_iterator { + using super = join_iterator; + using join_type = left_outer_join_t; + + template + void operator()(const L &l) { + l(*this); + this->super::operator()(l); + } + }; + + template + struct join_iterator, Tail...> : public join_iterator { + using super = join_iterator; + using join_type = inner_join_t; + + template + void operator()(const L &l) { + l(*this); + this->super::operator()(l); + } + }; + } +} +#pragma once + +#include // std::string +#include // std::make_tuple, std::tuple_size +#include // std::forward, std::is_base_of, std::enable_if +#include // std::unique_ptr +#include // std::vector + +// #include "conditions.h" + +// #include "operators.h" + +// #include "is_base_of_template.h" + +#include // std::true_type, std::false_type, std::declval + +namespace sqlite_orm { + + namespace internal { + + /* + * This is because of bug in MSVC, for more information, please visit + * https://stackoverflow.com/questions/34672441/stdis-base-of-for-template-classes/34672753#34672753 + */ +#if defined(_MSC_VER) + template class Base, typename Derived> + struct is_base_of_template_impl { + template + static constexpr std::true_type test(const Base *); + + static constexpr std::false_type test(...); + + using type = decltype(test(std::declval())); + }; + + template class Base> + using is_base_of_template = typename is_base_of_template_impl::type; + +#else + template class C, typename... Ts> + std::true_type is_base_of_template_impl(const C *); + + template class C> + std::false_type is_base_of_template_impl(...); + + template class C> + using is_base_of_template = decltype(is_base_of_template_impl(std::declval())); +#endif + } +} + +namespace sqlite_orm { + + namespace internal { + + template + struct unique_ptr_result_of {}; + + /** + * Base class for operator overloading + * R - return type + * S - class with operator std::string + * Args - function arguments types + */ + template + struct core_function_t : S, internal::arithmetic_t { + using return_type = R; + using string_type = S; + using args_type = std::tuple; + + static constexpr const size_t args_size = std::tuple_size::value; + + args_type args; + + core_function_t(args_type &&args_) : args(std::move(args_)) {} + }; + + struct length_string { + operator std::string() const { + return "LENGTH"; + } + }; + + struct abs_string { + operator std::string() const { + return "ABS"; + } + }; + + struct lower_string { + operator std::string() const { + return "LOWER"; + } + }; + + struct upper_string { + operator std::string() const { + return "UPPER"; + } + }; + + struct changes_string { + operator std::string() const { + return "CHANGES"; + } + }; + + struct trim_string { + operator std::string() const { + return "TRIM"; + } + }; + + struct ltrim_string { + operator std::string() const { + return "LTRIM"; + } + }; + + struct rtrim_string { + operator std::string() const { + return "RTRIM"; + } + }; + + struct hex_string { + operator std::string() const { + return "HEX"; + } + }; + + struct quote_string { + operator std::string() const { + return "QUOTE"; + } + }; + + struct randomblob_string { + operator std::string() const { + return "RANDOMBLOB"; + } + }; + + struct instr_string { + operator std::string() const { + return "INSTR"; + } + }; + + struct replace_string { + operator std::string() const { + return "REPLACE"; + } + }; + + struct round_string { + operator std::string() const { + return "ROUND"; + } + }; + +#if SQLITE_VERSION_NUMBER >= 3007016 + + struct char_string { + operator std::string() const { + return "CHAR"; + } + }; + + struct random_string { + operator std::string() const { + return "RANDOM"; + } + }; + +#endif + + struct coalesce_string { + operator std::string() const { + return "COALESCE"; + } + }; + + struct date_string { + operator std::string() const { + return "DATE"; + } + }; + + struct time_string { + operator std::string() const { + return "TIME"; + } + }; + + struct datetime_string { + operator std::string() const { + return "DATETIME"; + } + }; + + struct julianday_string { + operator std::string() const { + return "JULIANDAY"; + } + }; + + struct strftime_string { + operator std::string() const { + return "STRFTIME"; + } + }; + + struct zeroblob_string { + operator std::string() const { + return "ZEROBLOB"; + } + }; + + struct substr_string { + operator std::string() const { + return "SUBSTR"; + } + }; +#ifdef SQLITE_SOUNDEX + struct soundex_string { + operator std::string() const { + return "SOUNDEX"; + } + }; +#endif + struct total_string { + operator std::string() const { + return "TOTAL"; + } + }; + + struct sum_string { + operator std::string() const { + return "SUM"; + } + }; + + struct count_string { + operator std::string() const { + return "COUNT"; + } + }; + + /** + * T is use to specify type explicitly for queries like + * SELECT COUNT(*) FROM table_name; + * T can be omitted with void. + */ + template + struct count_asterisk_t : count_string { + using type = T; + }; + + /** + * The same thing as count() but without T arg. + * Is used in cases like this: + * SELECT cust_code, cust_name, cust_city, grade + * FROM customer + * WHERE grade=2 AND EXISTS + * (SELECT COUNT(*) + * FROM customer + * WHERE grade=2 + * GROUP BY grade + * HAVING COUNT(*)>2); + * `c++` + * auto rows = + * storage.select(columns(&Customer::code, &Customer::name, &Customer::city, &Customer::grade), + * where(is_equal(&Customer::grade, 2) + * and exists(select(count(), + * where(is_equal(&Customer::grade, 2)), + * group_by(&Customer::grade), + * having(greater_than(count(), 2)))))); + */ + struct count_asterisk_without_type : count_string {}; + + struct avg_string { + operator std::string() const { + return "AVG"; + } + }; + + struct max_string { + operator std::string() const { + return "MAX"; + } + }; + + struct min_string { + operator std::string() const { + return "MIN"; + } + }; + + struct group_concat_string { + operator std::string() const { + return "GROUP_CONCAT"; + } + }; + + } + + /** + * Cute operators for core functions + */ + + template< + class F, + class R, + typename = typename std::enable_if::value>::type> + internal::lesser_than_t operator<(F f, R r) { + return {std::move(f), std::move(r)}; + } + + template< + class F, + class R, + typename = typename std::enable_if::value>::type> + internal::lesser_or_equal_t operator<=(F f, R r) { + return {std::move(f), std::move(r)}; + } + + template< + class F, + class R, + typename = typename std::enable_if::value>::type> + internal::greater_than_t operator>(F f, R r) { + return {std::move(f), std::move(r)}; + } + + template< + class F, + class R, + typename = typename std::enable_if::value>::type> + internal::greater_or_equal_t operator>=(F f, R r) { + return {std::move(f), std::move(r)}; + } + + template< + class F, + class R, + typename = typename std::enable_if::value>::type> + internal::is_equal_t operator==(F f, R r) { + return {std::move(f), std::move(r)}; + } + + template< + class F, + class R, + typename = typename std::enable_if::value>::type> + internal::is_not_equal_t operator!=(F f, R r) { + return {std::move(f), std::move(r)}; + } + + /** + * LENGTH(x) function https://sqlite.org/lang_corefunc.html#length + */ + template + internal::core_function_t length(T t) { + std::tuple args{std::forward(t)}; + return {move(args)}; + } + + /** + * ABS(x) function https://sqlite.org/lang_corefunc.html#abs + */ + template + internal::core_function_t, internal::abs_string, T> abs(T t) { + std::tuple args{std::forward(t)}; + return {move(args)}; + } + + /** + * LOWER(x) function https://sqlite.org/lang_corefunc.html#lower + */ + template + internal::core_function_t lower(T t) { + std::tuple args{std::forward(t)}; + return {move(args)}; + } + + /** + * UPPER(x) function https://sqlite.org/lang_corefunc.html#upper + */ + template + internal::core_function_t upper(T t) { + std::tuple args{std::forward(t)}; + return {move(args)}; + } + + /** + * CHANGES() function https://sqlite.org/lang_corefunc.html#changes + */ + inline internal::core_function_t changes() { + return {{}}; + } + + /** + * TRIM(X) function https://sqlite.org/lang_corefunc.html#trim + */ + template + internal::core_function_t trim(T t) { + std::tuple args{std::forward(t)}; + return {move(args)}; + } + + /** + * TRIM(X,Y) function https://sqlite.org/lang_corefunc.html#trim + */ + template + internal::core_function_t trim(X x, Y y) { + std::tuple args{std::forward(x), std::forward(y)}; + return {move(args)}; + } + + /** + * LTRIM(X) function https://sqlite.org/lang_corefunc.html#ltrim + */ + template + internal::core_function_t ltrim(X x) { + std::tuple args{std::forward(x)}; + return {move(args)}; + } + + /** + * LTRIM(X,Y) function https://sqlite.org/lang_corefunc.html#ltrim + */ + template + internal::core_function_t ltrim(X x, Y y) { + std::tuple args{std::forward(x), std::forward(y)}; + return {move(args)}; + } + + /** + * RTRIM(X) function https://sqlite.org/lang_corefunc.html#rtrim + */ + template + internal::core_function_t rtrim(X x) { + std::tuple args{std::forward(x)}; + return {move(args)}; + } + + /** + * RTRIM(X,Y) function https://sqlite.org/lang_corefunc.html#rtrim + */ + template + internal::core_function_t rtrim(X x, Y y) { + std::tuple args{std::forward(x), std::forward(y)}; + return {move(args)}; + } + + /** + * HEX(X) function https://sqlite.org/lang_corefunc.html#hex + */ + template + internal::core_function_t hex(X x) { + std::tuple args{std::forward(x)}; + return {move(args)}; + } + + /** + * QUOTE(X) function https://sqlite.org/lang_corefunc.html#quote + */ + template + internal::core_function_t quote(X x) { + std::tuple args{std::forward(x)}; + return {move(args)}; + } + + /** + * RANDOMBLOB(X) function https://sqlite.org/lang_corefunc.html#randomblob + */ + template + internal::core_function_t, internal::randomblob_string, X> randomblob(X x) { + std::tuple args{std::forward(x)}; + return {move(args)}; + } + + /** + * INSTR(X) function https://sqlite.org/lang_corefunc.html#instr + */ + template + internal::core_function_t instr(X x, Y y) { + std::tuple args{std::forward(x), std::forward(y)}; + return {move(args)}; + } + + /** + * REPLACE(X) function https://sqlite.org/lang_corefunc.html#replace + */ + template + internal::core_function_t replace(X x, Y y, Z z) { + std::tuple args{std::forward(x), std::forward(y), std::forward(z)}; + return {move(args)}; + } + + /** + * ROUND(X) function https://sqlite.org/lang_corefunc.html#round + */ + template + internal::core_function_t round(X x) { + std::tuple args{std::forward(x)}; + return {move(args)}; + } + + /** + * ROUND(X, Y) function https://sqlite.org/lang_corefunc.html#round + */ + template + internal::core_function_t round(X x, Y y) { + std::tuple args{std::forward(x), std::forward(y)}; + return {move(args)}; + } + +#if SQLITE_VERSION_NUMBER >= 3007016 + + /** + * CHAR(X1,X2,...,XN) function https://sqlite.org/lang_corefunc.html#char + */ + template + internal::core_function_t char_(Args... args) { + return {std::make_tuple(std::forward(args)...)}; + } + + /** + * RANDOM() function https://www.sqlite.org/lang_corefunc.html#random + */ + inline internal::core_function_t random() { + return {{}}; + } + +#endif + + /** + * COALESCE(X,Y,...) function https://www.sqlite.org/lang_corefunc.html#coalesce + */ + template + internal::core_function_t coalesce(Args... args) { + return {std::make_tuple(std::forward(args)...)}; + } + + /** + * DATE(timestring, modifier, modifier, ...) function https://www.sqlite.org/lang_datefunc.html + */ + template + internal::core_function_t date(Args... args) { + std::tuple t{std::forward(args)...}; + return {move(t)}; + } + + /** + * TIME(timestring, modifier, modifier, ...) function https://www.sqlite.org/lang_datefunc.html + */ + template + internal::core_function_t time(Args... args) { + std::tuple t{std::forward(args)...}; + return {move(t)}; + } + + /** + * DATETIME(timestring, modifier, modifier, ...) function https://www.sqlite.org/lang_datefunc.html + */ + template + internal::core_function_t datetime(Args... args) { + std::tuple t{std::forward(args)...}; + return {move(t)}; + } + + /** + * JULIANDAY(timestring, modifier, modifier, ...) function https://www.sqlite.org/lang_datefunc.html + */ + template + internal::core_function_t julianday(Args... args) { + std::tuple t{std::forward(args)...}; + return {move(t)}; + } + + /** + * STRFTIME(timestring, modifier, modifier, ...) function https://www.sqlite.org/lang_datefunc.html + */ + template + internal::core_function_t strftime(Args... args) { + std::tuple t{std::forward(args)...}; + return {move(t)}; + } + + /** + * ZEROBLOB(N) function https://www.sqlite.org/lang_corefunc.html#zeroblob + */ + template + internal::core_function_t, internal::zeroblob_string, N> zeroblob(N n) { + std::tuple args{std::forward(n)}; + return {move(args)}; + } + + /** + * SUBSTR(X,Y) function https://www.sqlite.org/lang_corefunc.html#substr + */ + template + internal::core_function_t substr(X x, Y y) { + std::tuple args{std::forward(x), std::forward(y)}; + return {move(args)}; + } + + /** + * SUBSTR(X,Y,Z) function https://www.sqlite.org/lang_corefunc.html#substr + */ + template + internal::core_function_t substr(X x, Y y, Z z) { + std::tuple args{std::forward(x), std::forward(y), std::forward(z)}; + return {move(args)}; + } + +#ifdef SQLITE_SOUNDEX + /** + * SOUNDEX(X) function https://www.sqlite.org/lang_corefunc.html#soundex + */ + template + internal::core_function_t soundex(X x) { + std::tuple args{std::forward(x)}; + return {move(args)}; + } +#endif + + /** + * TOTAL(X) aggregate function. + */ + template + internal::core_function_t total(X x) { + std::tuple args{std::forward(x)}; + return {move(args)}; + } + + /** + * SUM(X) aggregate function. + */ + template + internal::core_function_t, internal::sum_string, X> sum(X x) { + std::tuple args{std::forward(x)}; + return {move(args)}; + } + + /** + * COUNT(X) aggregate function. + */ + template + internal::core_function_t count(X x) { + std::tuple args{std::forward(x)}; + return {move(args)}; + } + + /** + * COUNT(*) without FROM function. + */ + inline internal::count_asterisk_without_type count() { + return {}; + } + + /** + * COUNT(*) with FROM function. Specified type T will be serializeed as + * a from argument. + */ + template + internal::count_asterisk_t count() { + return {}; + } + + /** + * AVG(X) aggregate function. + */ + template + internal::core_function_t avg(X x) { + std::tuple args{std::forward(x)}; + return {move(args)}; + } + + /** + * MAX(X) aggregate function. + */ + template + internal::core_function_t, internal::max_string, X> max(X x) { + std::tuple args{std::forward(x)}; + return {move(args)}; + } + + /** + * MIN(X) aggregate function. + */ + template + internal::core_function_t, internal::min_string, X> min(X x) { + std::tuple args{std::forward(x)}; + return {move(args)}; + } + + /** + * GROUP_CONCAT(X) aggregate function. + */ + template + internal::core_function_t group_concat(X x) { + std::tuple args{std::forward(x)}; + return {move(args)}; + } + + /** + * GROUP_CONCAT(X, Y) aggregate function. + */ + template + internal::core_function_t group_concat(X x, Y y) { + std::tuple args{std::forward(x), std::forward(y)}; + return {move(args)}; + } + + template::value + + std::is_base_of::value > + 0)>::type> + internal::add_t operator+(L l, R r) { + return {std::move(l), std::move(r)}; + } + + template::value + + std::is_base_of::value > + 0)>::type> + internal::sub_t operator-(L l, R r) { + return {std::move(l), std::move(r)}; + } + + template::value + + std::is_base_of::value > + 0)>::type> + internal::mul_t operator*(L l, R r) { + return {std::move(l), std::move(r)}; + } + + template::value + + std::is_base_of::value > + 0)>::type> + internal::div_t operator/(L l, R r) { + return {std::move(l), std::move(r)}; + } + + template::value + + std::is_base_of::value > + 0)>::type> + internal::mod_t operator%(L l, R r) { + return {std::move(l), std::move(r)}; + } +} +#pragma once + +namespace sqlite_orm { + + namespace internal { + + /** + * Cute class used to compare setters/getters and member pointers with each other. + */ + template + struct typed_comparator { + bool operator()(const L &, const R &) const { + return false; + } + }; + + template + struct typed_comparator { + bool operator()(const O &lhs, const O &rhs) const { + return lhs == rhs; + } + }; + + template + bool compare_any(const L &lhs, const R &rhs) { + return typed_comparator()(lhs, rhs); + } + } +} +#pragma once + +#include // std::string +#include // std::declval +#include // std::tuple, std::get, std::tuple_size + +// #include "is_base_of_template.h" + +// #include "tuple_helper.h" + +// #include "optional_container.h" + +namespace sqlite_orm { + + namespace internal { + + /** + * DISCTINCT generic container. + */ + template + struct distinct_t { + T t; + + operator std::string() const { + return "DISTINCT"; + } + }; + + /** + * ALL generic container. + */ + template + struct all_t { + T t; + + operator std::string() const { + return "ALL"; + } + }; + + template + struct columns_t { + using columns_type = std::tuple; + + columns_type columns; + bool distinct = false; + + static constexpr const int count = std::tuple_size::value; + }; + + struct set_string { + operator std::string() const { + return "SET"; + } + }; + + template + struct set_t : set_string { + using assigns_type = std::tuple; + + assigns_type assigns; + + set_t(assigns_type assigns_) : assigns(move(assigns_)) {} + }; + + /** + * This class is used to store explicit mapped type T and its column descriptor (member pointer/getter/setter). + * Is useful when mapped type is derived from other type and base class has members mapped to a storage. + */ + template + struct column_pointer { + using type = T; + using field_type = F; + + field_type field; + }; + + /** + * Subselect object type. + */ + template + struct select_t { + using return_type = T; + using conditions_type = std::tuple; + + return_type col; + conditions_type conditions; + bool highest_level = false; + }; + + /** + * Base for UNION, UNION ALL, EXCEPT and INTERSECT + */ + template + struct compound_operator { + using left_type = L; + using right_type = R; + + left_type left; + right_type right; + + compound_operator(left_type l, right_type r) : left(std::move(l)), right(std::move(r)) { + this->left.highest_level = true; + this->right.highest_level = true; + } + }; + + struct union_base { + bool all = false; + + operator std::string() const { + if(!this->all) { + return "UNION"; + } else { + return "UNION ALL"; + } + } + }; + + /** + * UNION object type. + */ + template + struct union_t : public compound_operator, union_base { + using left_type = typename compound_operator::left_type; + using right_type = typename compound_operator::right_type; + + union_t(left_type l, right_type r, decltype(all) all) : + compound_operator(std::move(l), std::move(r)), union_base{all} {} + + union_t(left_type l, right_type r) : union_t(std::move(l), std::move(r), false) {} + }; + + /** + * EXCEPT object type. + */ + template + struct except_t : public compound_operator { + using super = compound_operator; + using left_type = typename super::left_type; + using right_type = typename super::right_type; + + using super::super; + + operator std::string() const { + return "EXCEPT"; + } + }; + + /** + * INTERSECT object type. + */ + template + struct intersect_t : public compound_operator { + using super = compound_operator; + using left_type = typename super::left_type; + using right_type = typename super::right_type; + + using super::super; + + operator std::string() const { + return "INTERSECT"; + } + }; + + /** + * Generic way to get DISTINCT value from any type. + */ + template + bool get_distinct(const T &) { + return false; + } + + template + bool get_distinct(const columns_t &cols) { + return cols.distinct; + } + + template + struct asterisk_t { + using type = T; + }; + + template + struct object_t { + using type = T; + }; + + template + struct then_t { + using expression_type = T; + + expression_type expression; + }; + + template + struct simple_case_t { + using return_type = R; + using case_expression_type = T; + using args_type = std::tuple; + using else_expression_type = E; + + optional_container case_expression; + args_type args; + optional_container else_expression; + }; + + /** + * T is a case expression type + * E is else type (void is ELSE is omitted) + * Args... is a pack of WHEN expressions + */ + template + struct simple_case_builder { + using return_type = R; + using case_expression_type = T; + using args_type = std::tuple; + using else_expression_type = E; + + optional_container case_expression; + args_type args; + optional_container else_expression; + + template + simple_case_builder> when(W w, then_t
t) { + using result_args_type = std::tuple>; + std::pair newPair{std::move(w), std::move(t.expression)}; + result_args_type result_args = + std::tuple_cat(std::move(this->args), std::move(std::make_tuple(newPair))); + std::get::value - 1>(result_args) = std::move(newPair); + return {std::move(this->case_expression), std::move(result_args), std::move(this->else_expression)}; + } + + simple_case_t end() { + return {std::move(this->case_expression), std::move(args), std::move(this->else_expression)}; + } + + template + simple_case_builder else_(El el) { + return {{std::move(this->case_expression)}, std::move(args), {std::move(el)}}; + } + }; + + template + void validate_conditions() { + static_assert(count_tuple::value <= 1, "a single query cannot contain > 1 WHERE blocks"); + static_assert(count_tuple::value <= 1, "a single query cannot contain > 1 GROUP BY blocks"); + static_assert(count_tuple::value <= 1, "a single query cannot contain > 1 ORDER BY blocks"); + static_assert(count_tuple::value <= 1, "a single query cannot contain > 1 LIMIT blocks"); + } + } + + template + internal::then_t then(T t) { + return {std::move(t)}; + } + + template + internal::simple_case_builder case_(T t) { + return {{std::move(t)}}; + } + + template + internal::simple_case_builder case_() { + return {}; + } + + template + internal::distinct_t distinct(T t) { + return {std::move(t)}; + } + + template + internal::all_t all(T t) { + return {std::move(t)}; + } + + template + internal::columns_t distinct(internal::columns_t cols) { + cols.distinct = true; + return cols; + } + + /** + * SET keyword used in UPDATE ... SET queries. + * Args must have `assign_t` type. E.g. set(assign(&User::id, 5)) or set(c(&User::id) = 5) + */ + template + internal::set_t set(Args... args) { + using arg_tuple = std::tuple; + static_assert(std::tuple_size::value == + internal::count_tuple::value, + "set function accepts assign operators only"); + return {std::make_tuple(std::forward(args)...)}; + } + + template + internal::columns_t columns(Args... args) { + return {std::make_tuple(std::forward(args)...)}; + } + + /** + * Use it like this: + * struct MyType : BaseType { ... }; + * storage.select(column(&BaseType::id)); + */ + template + internal::column_pointer column(F f) { + return {std::move(f)}; + } + + /** + * Public function for subselect query. Is useful in UNION queries. + */ + template + internal::select_t select(T t, Args... args) { + using args_tuple = std::tuple; + internal::validate_conditions(); + return {std::move(t), std::make_tuple(std::forward(args)...)}; + } + + /** + * Public function for UNION operator. + * lhs and rhs are subselect objects. + * Look through example in examples/union.cpp + */ + template + internal::union_t union_(L lhs, R rhs) { + return {std::move(lhs), std::move(rhs)}; + } + + /** + * Public function for EXCEPT operator. + * lhs and rhs are subselect objects. + * Look through example in examples/except.cpp + */ + template + internal::except_t except(L lhs, R rhs) { + return {std::move(lhs), std::move(rhs)}; + } + + template + internal::intersect_t intersect(L lhs, R rhs) { + return {std::move(lhs), std::move(rhs)}; + } + + /** + * Public function for UNION ALL operator. + * lhs and rhs are subselect objects. + * Look through example in examples/union.cpp + */ + template + internal::union_t union_all(L lhs, R rhs) { + return {std::move(lhs), std::move(rhs), true}; + } + + /** + * SELECT * FROM T function. + * T is typed mapped to a storage. + * Example: auto rows = storage.select(asterisk()); + * // decltype(rows) is std::vector> + * If you need to fetch result as objects not tuple please use `object` instead. + */ + template + internal::asterisk_t asterisk() { + return {}; + } + + /** + * SELECT * FROM T function. + * T is typed mapped to a storage. + * Example: auto rows = storage.select(object()); + * // decltype(rows) is std::vector + * If you need to fetch result as tuples not objects please use `asterisk` instead. + */ + template + internal::object_t object() { + return {}; + } +} +#pragma once + +#include // std::enable_if, std::is_member_pointer + +// #include "select_constraints.h" + +// #include "column.h" + +namespace sqlite_orm { + + namespace internal { + + /** + * Trait class used to define table mapped type by setter/getter/member + * T - member pointer + */ + template + struct table_type; + + template + struct table_type::value && + !std::is_member_function_pointer::value>::type> { + using type = O; + }; + + template + struct table_type::value>::type> { + using type = typename getter_traits::object_type; + }; + + template + struct table_type::value>::type> { + using type = typename setter_traits::object_type; + }; + + template + struct table_type, void> { + using type = T; + }; + } +} +#pragma once + +#include // std::string + +namespace sqlite_orm { + + struct table_info { + int cid = 0; + std::string name; + std::string type; + bool notnull = false; + std::string dflt_value; + int pk = 0; + }; + +} +#pragma once + +#include "sqlite3.h" + +namespace sqlite_orm { + + /** + * Guard class which finalizes `sqlite3_stmt` in dtor + */ + struct statement_finalizer { + sqlite3_stmt *stmt = nullptr; + + statement_finalizer(decltype(stmt) stmt_) : stmt(stmt_) {} + + inline ~statement_finalizer() { + sqlite3_finalize(this->stmt); + } + }; +} +#pragma once + +namespace sqlite_orm { + + /** + * Helper classes used by statement_binder and row_extractor. + */ + struct int_or_smaller_tag {}; + struct bigint_tag {}; + struct real_tag {}; + + template + struct arithmetic_tag { + using type = std::conditional_t::value, + // Integer class + std::conditional_t, + // Floating-point class + real_tag>; + }; + + template + using arithmetic_tag_t = typename arithmetic_tag::type; +} +#pragma once + +#include "sqlite3.h" +#include // std::enable_if_t, std::is_arithmetic, std::is_same, std::true_type, std::false_type +#include // std::string, std::wstring +#ifndef SQLITE_ORM_OMITS_CODECVT +#include // std::codecvt_utf8_utf16 +#endif // SQLITE_ORM_OMITS_CODECVT +#include // std::vector +#include // std::nullptr_t +#include // std::declval +#include // std::wstring_convert + +// #include "is_std_ptr.h" + +namespace sqlite_orm { + + /** + * Specialization for optional type (std::shared_ptr / std::unique_ptr). + */ + template + struct is_std_ptr : std::false_type {}; + + template + struct is_std_ptr> : std::true_type { + using element_type = T; + + static std::shared_ptr make(const T &v) { + return std::make_shared(v); + } + }; + + template + struct is_std_ptr> : std::true_type { + using element_type = T; + + static std::unique_ptr make(const T &v) { + return std::make_unique(v); + } + }; +} + +namespace sqlite_orm { + + /** + * Helper class used for binding fields to sqlite3 statements. + */ + template + struct statement_binder : std::false_type {}; + + /** + * Specialization for arithmetic types. + */ + template + struct statement_binder::value>> { + int bind(sqlite3_stmt *stmt, int index, const V &value) { + return bind(stmt, index, value, tag()); + } + + private: + using tag = arithmetic_tag_t; + + int bind(sqlite3_stmt *stmt, int index, const V &value, const int_or_smaller_tag &) { + return sqlite3_bind_int(stmt, index, static_cast(value)); + } + + int bind(sqlite3_stmt *stmt, int index, const V &value, const bigint_tag &) { + return sqlite3_bind_int64(stmt, index, static_cast(value)); + } + + int bind(sqlite3_stmt *stmt, int index, const V &value, const real_tag &) { + return sqlite3_bind_double(stmt, index, static_cast(value)); + } + }; + + /** + * Specialization for std::string and C-string. + */ + template + struct statement_binder< + V, + std::enable_if_t::value || std::is_same::value>> { + int bind(sqlite3_stmt *stmt, int index, const V &value) { + return sqlite3_bind_text(stmt, index, string_data(value), -1, SQLITE_TRANSIENT); + } + + private: + const char *string_data(const std::string &s) const { + return s.c_str(); + } + + const char *string_data(const char *s) const { + return s; + } + }; + +#ifndef SQLITE_ORM_OMITS_CODECVT + /** + * Specialization for std::wstring and C-wstring. + */ + template + struct statement_binder< + V, + std::enable_if_t::value || std::is_same::value>> { + int bind(sqlite3_stmt *stmt, int index, const V &value) { + std::wstring_convert> converter; + std::string utf8Str = converter.to_bytes(value); + return statement_binder().bind(stmt, index, utf8Str); + } + }; +#endif // SQLITE_ORM_OMITS_CODECVT + + /** + * Specialization for std::nullptr_t. + */ + template<> + struct statement_binder { + int bind(sqlite3_stmt *stmt, int index, const std::nullptr_t &) { + return sqlite3_bind_null(stmt, index); + } + }; + +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + template<> + struct statement_binder { + int bind(sqlite3_stmt *stmt, int index, const std::nullopt_t &) { + return sqlite3_bind_null(stmt, index); + } + }; +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED + + template + struct statement_binder::value>> { + using value_type = typename is_std_ptr::element_type; + + int bind(sqlite3_stmt *stmt, int index, const V &value) { + if(value) { + return statement_binder().bind(stmt, index, *value); + } else { + return statement_binder().bind(stmt, index, nullptr); + } + } + }; + + /** + * Specialization for optional type (std::vector). + */ + template<> + struct statement_binder, void> { + int bind(sqlite3_stmt *stmt, int index, const std::vector &value) { + if(value.size()) { + return sqlite3_bind_blob(stmt, + index, + (const void *)&value.front(), + int(value.size()), + SQLITE_TRANSIENT); + } else { + return sqlite3_bind_blob(stmt, index, "", 0, SQLITE_TRANSIENT); + } + } + }; + +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + template + struct statement_binder, void> { + using value_type = T; + + int bind(sqlite3_stmt *stmt, int index, const std::optional &value) { + if(value) { + return statement_binder().bind(stmt, index, *value); + } else { + return statement_binder().bind(stmt, index, std::nullopt); + } + } + }; +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED + + namespace internal { + + template + using is_bindable = std::integral_constant>::value>; + + struct conditional_binder_base { + sqlite3_stmt *stmt = nullptr; + int &index; + + conditional_binder_base(decltype(stmt) stmt_, decltype(index) index_) : stmt(stmt_), index(index_) {} + }; + + template + struct conditional_binder; + + template + struct conditional_binder : conditional_binder_base { + + using conditional_binder_base::conditional_binder_base; + + int operator()(const T &t) const { + return statement_binder().bind(this->stmt, this->index++, t); + } + }; + + template + struct conditional_binder : conditional_binder_base { + using conditional_binder_base::conditional_binder_base; + + int operator()(const T &) const { + return SQLITE_OK; + } + }; + + template + struct bindable_filter_single; + + template + struct bindable_filter_single::value>::type> { + using type = std::tuple; + }; + + template + struct bindable_filter_single::value>::type> { + using type = std::tuple<>; + }; + + template + struct bindable_filter; + + template + struct bindable_filter> { + using type = typename conc_tuple::type...>::type; + }; + } +} +#pragma once + +#include "sqlite3.h" +#include // std::enable_if_t, std::is_arithmetic, std::is_same, std::enable_if +#include // atof, atoi, atoll +#include // std::string, std::wstring +#ifndef SQLITE_ORM_OMITS_CODECVT +#include // std::wstring_convert, std::codecvt_utf8_utf16 +#endif // SQLITE_ORM_OMITS_CODECVT +#include // std::vector +#include // strlen +#include // std::copy +#include // std::back_inserter +#include // std::tuple, std::tuple_size, std::tuple_element + +// #include "arithmetic_tag.h" + +// #include "journal_mode.h" + +#include // std::string +#include // std::unique_ptr +#include // std::array +#include // std::transform +#include // std::toupper + +namespace sqlite_orm { + +/** + * Caps case cause of 1) delete keyword; 2) https://www.sqlite.org/pragma.html#pragma_journal_mode original spelling + */ +#ifdef DELETE +#undef DELETE +#endif + enum class journal_mode : signed char { + DELETE = 0, + TRUNCATE = 1, + PERSIST = 2, + MEMORY = 3, + WAL = 4, + OFF = 5, + }; + + namespace internal { + + inline const std::string &to_string(journal_mode j) { + static std::string res[] = { + "DELETE", + "TRUNCATE", + "PERSIST", + "MEMORY", + "WAL", + "OFF", + }; + return res[static_cast(j)]; + } + + inline std::unique_ptr journal_mode_from_string(const std::string &str) { + std::string upper_str; + std::transform(str.begin(), str.end(), std::back_inserter(upper_str), [](char c) { + return static_cast(std::toupper(static_cast(c))); + }); + static std::array all = { + journal_mode::DELETE, + journal_mode::TRUNCATE, + journal_mode::PERSIST, + journal_mode::MEMORY, + journal_mode::WAL, + journal_mode::OFF, + }; + for(auto j: all) { + if(to_string(j) == upper_str) { + return std::make_unique(j); + } + } + return {}; + } + } +} + +// #include "error_code.h" + +namespace sqlite_orm { + + /** + * Helper class used to cast values from argv to V class + * which depends from column type. + * + */ + template + struct row_extractor { + // used in sqlite3_exec (select) + V extract(const char *row_value); + + // used in sqlite_column (iteration, get_all) + V extract(sqlite3_stmt *stmt, int columnIndex); + }; + + /** + * Specialization for arithmetic types. + */ + template + struct row_extractor::value>> { + V extract(const char *row_value) { + return extract(row_value, tag()); + } + + V extract(sqlite3_stmt *stmt, int columnIndex) { + return extract(stmt, columnIndex, tag()); + } + + private: + using tag = arithmetic_tag_t; + + V extract(const char *row_value, const int_or_smaller_tag &) { + return static_cast(atoi(row_value)); + } + + V extract(sqlite3_stmt *stmt, int columnIndex, const int_or_smaller_tag &) { + return static_cast(sqlite3_column_int(stmt, columnIndex)); + } + + V extract(const char *row_value, const bigint_tag &) { + return static_cast(atoll(row_value)); + } + + V extract(sqlite3_stmt *stmt, int columnIndex, const bigint_tag &) { + return static_cast(sqlite3_column_int64(stmt, columnIndex)); + } + + V extract(const char *row_value, const real_tag &) { + return static_cast(atof(row_value)); + } + + V extract(sqlite3_stmt *stmt, int columnIndex, const real_tag &) { + return static_cast(sqlite3_column_double(stmt, columnIndex)); + } + }; + + /** + * Specialization for std::string. + */ + template<> + struct row_extractor { + std::string extract(const char *row_value) { + if(row_value) { + return row_value; + } else { + return {}; + } + } + + std::string extract(sqlite3_stmt *stmt, int columnIndex) { + auto cStr = (const char *)sqlite3_column_text(stmt, columnIndex); + if(cStr) { + return cStr; + } else { + return {}; + } + } + }; +#ifndef SQLITE_ORM_OMITS_CODECVT + /** + * Specialization for std::wstring. + */ + template<> + struct row_extractor { + std::wstring extract(const char *row_value) { + if(row_value) { + std::wstring_convert> converter; + return converter.from_bytes(row_value); + } else { + return {}; + } + } + + std::wstring extract(sqlite3_stmt *stmt, int columnIndex) { + auto cStr = (const char *)sqlite3_column_text(stmt, columnIndex); + if(cStr) { + std::wstring_convert> converter; + return converter.from_bytes(cStr); + } else { + return {}; + } + } + }; +#endif // SQLITE_ORM_OMITS_CODECVT + + template + struct row_extractor::value>> { + using value_type = typename is_std_ptr::element_type; + + V extract(const char *row_value) { + if(row_value) { + return is_std_ptr::make(row_extractor().extract(row_value)); + } else { + return {}; + } + } + + V extract(sqlite3_stmt *stmt, int columnIndex) { + auto type = sqlite3_column_type(stmt, columnIndex); + if(type != SQLITE_NULL) { + return is_std_ptr::make(row_extractor().extract(stmt, columnIndex)); + } else { + return {}; + } + } + }; + +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + template + struct row_extractor, void> { + using value_type = T; + + std::optional extract(const char *row_value) { + if(row_value) { + return std::make_optional(row_extractor().extract(row_value)); + } else { + return std::nullopt; + } + } + + std::optional extract(sqlite3_stmt *stmt, int columnIndex) { + auto type = sqlite3_column_type(stmt, columnIndex); + if(type != SQLITE_NULL) { + return std::make_optional(row_extractor().extract(stmt, columnIndex)); + } else { + return std::nullopt; + } + } + }; +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED + /** + * Specialization for std::vector. + */ + template<> + struct row_extractor> { + std::vector extract(const char *row_value) { + if(row_value) { + auto len = ::strlen(row_value); + return this->go(row_value, len); + } else { + return {}; + } + } + + std::vector extract(sqlite3_stmt *stmt, int columnIndex) { + auto bytes = static_cast(sqlite3_column_blob(stmt, columnIndex)); + auto len = static_cast(sqlite3_column_bytes(stmt, columnIndex)); + return this->go(bytes, len); + } + + protected: + std::vector go(const char *bytes, size_t len) { + if(len) { + std::vector res; + res.reserve(len); + std::copy(bytes, bytes + len, std::back_inserter(res)); + return res; + } else { + return {}; + } + } + }; + + template + struct row_extractor> { + + std::tuple extract(char **argv) { + std::tuple res; + this->extract::value>(res, argv); + return res; + } + + std::tuple extract(sqlite3_stmt *stmt, int /*columnIndex*/) { + std::tuple res; + this->extract::value>(res, stmt); + return res; + } + + protected: + template::type * = nullptr> + void extract(std::tuple &t, sqlite3_stmt *stmt) { + using tuple_type = typename std::tuple_element>::type; + std::get(t) = row_extractor().extract(stmt, I - 1); + this->extract(t, stmt); + } + + template::type * = nullptr> + void extract(std::tuple &, sqlite3_stmt *) { + //.. + } + + template::type * = nullptr> + void extract(std::tuple &t, char **argv) { + using tuple_type = typename std::tuple_element>::type; + std::get(t) = row_extractor().extract(argv[I - 1]); + this->extract(t, argv); + } + + template::type * = nullptr> + void extract(std::tuple &, char **) { + //.. + } + }; + + /** + * Specialization for journal_mode. + */ + template<> + struct row_extractor { + journal_mode extract(const char *row_value) { + if(row_value) { + if(auto res = internal::journal_mode_from_string(row_value)) { + return std::move(*res); + } else { + throw std::system_error(std::make_error_code(orm_error_code::incorrect_journal_mode_string)); + } + } else { + throw std::system_error(std::make_error_code(orm_error_code::incorrect_journal_mode_string)); + } + } + + journal_mode extract(sqlite3_stmt *stmt, int columnIndex) { + auto cStr = (const char *)sqlite3_column_text(stmt, columnIndex); + return this->extract(cStr); + } + }; +} +#pragma once + +#include + +namespace sqlite_orm { + + enum class sync_schema_result { + + /** + * created new table, table with the same tablename did not exist + */ + new_table_created, + + /** + * table schema is the same as storage, nothing to be done + */ + already_in_sync, + + /** + * removed excess columns in table (than storage) without dropping a table + */ + old_columns_removed, + + /** + * lacking columns in table (than storage) added without dropping a table + */ + new_columns_added, + + /** + * both old_columns_removed and new_columns_added + */ + new_columns_added_and_old_columns_removed, + + /** + * old table is dropped and new is recreated. Reasons : + * 1. delete excess columns in the table than storage if preseve = false + * 2. Lacking columns in the table cannot be added due to NULL and DEFAULT constraint + * 3. Reasons 1 and 2 both together + * 4. data_type mismatch between table and storage. + */ + dropped_and_recreated, + }; + + inline std::ostream &operator<<(std::ostream &os, sync_schema_result value) { + switch(value) { + case sync_schema_result::new_table_created: + return os << "new table created"; + case sync_schema_result::already_in_sync: + return os << "table and storage is already in sync."; + case sync_schema_result::old_columns_removed: + return os << "old excess columns removed"; + case sync_schema_result::new_columns_added: + return os << "new columns added"; + case sync_schema_result::new_columns_added_and_old_columns_removed: + return os << "old excess columns removed and new columns added"; + case sync_schema_result::dropped_and_recreated: + return os << "old table dropped and recreated"; + } + return os; + } +} +#pragma once + +#include // std::tuple, std::make_tuple +#include // std::string +#include // std::forward + +// #include "indexed_column.h" + +#include // std::string + +namespace sqlite_orm { + + namespace internal { + + template + struct indexed_column_t { + using column_type = C; + + column_type column_or_expression; + std::string _collation_name; + int _order = 0; // -1 = desc, 1 = asc, 0 = not specified + + indexed_column_t collate(std::string name) { + auto res = std::move(*this); + res._collation_name = move(name); + return res; + } + + indexed_column_t asc() { + auto res = std::move(*this); + res._order = 1; + return res; + } + + indexed_column_t desc() { + auto res = std::move(*this); + res._order = -1; + return res; + } + }; + + template + struct indexed_column_maker { + using type = indexed_column_t; + + indexed_column_t operator()(C col) const { + return {std::move(col)}; + } + }; + + template + struct indexed_column_maker> { + using type = indexed_column_t; + + indexed_column_t operator()(indexed_column_t col) const { + return std::move(col); + } + }; + + template + auto make_indexed_column(C col) { + indexed_column_maker maker; + return maker(std::move(col)); + } + + } + + /** + * Use this function to specify indexed column inside `make_index` function call. + * Example: make_index("index_name", indexed_column(&User::id).asc()) + */ + template + internal::indexed_column_t indexed_column(C column_or_expression) { + return {std::move(column_or_expression)}; + } + +} + +namespace sqlite_orm { + + namespace internal { + + struct index_base { + std::string name; + bool unique = false; + }; + + template + struct index_t : index_base { + using columns_type = std::tuple; + using object_type = void; + + index_t(std::string name, bool unique, columns_type columns_) : + index_base{move(name), unique}, columns(move(columns_)) {} + + columns_type columns; + }; + } + + template + internal::index_t::type...> make_index(const std::string &name, + Cols... cols) { + return {name, false, std::make_tuple(internal::make_indexed_column(cols)...)}; + } + + template + internal::index_t::type...> make_unique_index(const std::string &name, + Cols... cols) { + return {name, true, std::make_tuple(internal::make_indexed_column(cols)...)}; + } +} +#pragma once + +// #include "alias.h" + +namespace sqlite_orm { + + namespace internal { + + /** + * If T is alias than mapped_type_proxy::type is alias::type + * otherwise T is T. + */ + template + struct mapped_type_proxy { + using type = T; + }; + + template + struct mapped_type_proxy::value>::type> { + using type = typename T::type; + }; + } +} +#pragma once + +#include // std::string + +namespace sqlite_orm { + + namespace internal { + + struct rowid_t { + operator std::string() const { + return "rowid"; + } + }; + + struct oid_t { + operator std::string() const { + return "oid"; + } + }; + + struct _rowid_t { + operator std::string() const { + return "_rowid_"; + } + }; + + template + struct table_rowid_t : public rowid_t { + using type = T; + }; + + template + struct table_oid_t : public oid_t { + using type = T; + }; + template + struct table__rowid_t : public _rowid_t { + using type = T; + }; + + } + + inline internal::rowid_t rowid() { + return {}; + } + + inline internal::oid_t oid() { + return {}; + } + + inline internal::_rowid_t _rowid_() { + return {}; + } + + template + internal::table_rowid_t rowid() { + return {}; + } + + template + internal::table_oid_t oid() { + return {}; + } + + template + internal::table__rowid_t _rowid_() { + return {}; + } +} +#pragma once + +#include // std::enable_if, std::is_same, std::decay, std::is_arithmetic +#include // std::tuple +#include // std::reference_wrapper + +// #include "core_functions.h" + +// #include "select_constraints.h" + +// #include "operators.h" + +// #include "rowid.h" + +// #include "alias.h" + +// #include "column.h" + +// #include "storage_traits.h" +#include // std::is_same, std::enable_if, std::true_type, std::false_type, std::integral_constant +#include // std::tuple + +namespace sqlite_orm { + + namespace internal { + + template + struct storage_impl; + + template + struct table_t; + + namespace storage_traits { + + /** + * S - storage_impl type + * T - mapped or not mapped data type + */ + template + struct type_is_mapped_impl; + + /** + * S - storage + * T - mapped or not mapped data type + */ + template + struct type_is_mapped : type_is_mapped_impl {}; + + /** + * Final specialisation + */ + template + struct type_is_mapped_impl, T, void> : std::false_type {}; + + template + struct type_is_mapped_impl< + S, + T, + typename std::enable_if::value>::type> + : std::true_type {}; + + template + struct type_is_mapped_impl< + S, + T, + typename std::enable_if::value>::type> + : type_is_mapped_impl {}; + + /** + * S - storage_impl type + * T - mapped or not mapped data type + */ + template + struct storage_columns_count_impl; + + /** + * S - storage + * T - mapped or not mapped data type + */ + template + struct storage_columns_count : storage_columns_count_impl {}; + + /** + * Final specialisation + */ + template + struct storage_columns_count_impl, T, void> : std::integral_constant {}; + + template + struct storage_columns_count_impl< + S, + T, + typename std::enable_if::value>::type> + : std::integral_constant {}; + + template + struct storage_columns_count_impl< + S, + T, + typename std::enable_if::value>::type> + : storage_columns_count_impl {}; + + /** + * T - table type. + */ + template + struct table_types; + + /** + * type is std::tuple of field types of mapped colums. + */ + template + struct table_types> { + using type = std::tuple; + }; + + /** + * S - storage_impl type + * T - mapped or not mapped data type + */ + template + struct storage_mapped_columns_impl; + + /** + * S - storage + * T - mapped or not mapped data type + */ + template + struct storage_mapped_columns : storage_mapped_columns_impl {}; + + /** + * Final specialisation + */ + template + struct storage_mapped_columns_impl, T, void> { + using type = std::tuple<>; + }; + + template + struct storage_mapped_columns_impl< + S, + T, + typename std::enable_if::value>::type> { + using table_type = typename S::table_type; + using type = typename table_types::type; + }; + + template + struct storage_mapped_columns_impl< + S, + T, + typename std::enable_if::value>::type> + : storage_mapped_columns_impl {}; + + } + } +} + +namespace sqlite_orm { + + using int64 = sqlite_int64; + using uint64 = sqlite_uint64; + + namespace internal { + + /** + * This is a proxy class used to define what type must have result type depending on select + * arguments (member pointer, aggregate functions, etc). Below you can see specializations + * for different types. E.g. specialization for internal::length_t has `type` int cause + * LENGTH returns INTEGER in sqlite. Every column_result_t must have `type` type that equals + * c++ SELECT return type for T + * T - C++ type + * SFINAE - sfinae argument + */ + template + struct column_result_t; + + template + struct column_result_t::value && + !std::is_member_function_pointer::value>::type> { + using type = F; + }; + + /** + * Common case for all getter types. Getter types are defined in column.h file + */ + template + struct column_result_t::value>::type> { + using type = typename getter_traits::field_type; + }; + + /** + * Common case for all setter types. Setter types are defined in column.h file + */ + template + struct column_result_t::value>::type> { + using type = typename setter_traits::field_type; + }; + + template + struct column_result_t, void> { + using type = R; + }; + + template + struct column_result_t, S, X>, void> { + using type = std::unique_ptr::type>; + }; + + template + struct column_result_t, void> { + using type = int; + }; + + template + struct column_result_t { + using type = int; + }; + + template + struct column_result_t, void> { + using type = typename column_result_t::type; + }; + + template + struct column_result_t, void> { + using type = typename column_result_t::type; + }; + + template + struct column_result_t, void> { + using type = std::string; + }; + + template + struct column_result_t, void> { + using type = double; + }; + + template + struct column_result_t, void> { + using type = double; + }; + + template + struct column_result_t, void> { + using type = double; + }; + + template + struct column_result_t, void> { + using type = double; + }; + + template + struct column_result_t, void> { + using type = double; + }; + + template + struct column_result_t, void> { + using type = int; + }; + + template + struct column_result_t, void> { + using type = int; + }; + + template + struct column_result_t, void> { + using type = int; + }; + + template + struct column_result_t, void> { + using type = int; + }; + + template + struct column_result_t, void> { + using type = int; + }; + + template + struct column_result_t { + using type = int64; + }; + + template + struct column_result_t { + using type = int64; + }; + + template + struct column_result_t { + using type = int64; + }; + + template + struct column_result_t, void> { + using type = int64; + }; + + template + struct column_result_t, void> { + using type = int64; + }; + + template + struct column_result_t, void> { + using type = int64; + }; + + template + struct column_result_t, void> { + using type = typename column_result_t::type; + }; + + template + struct column_result_t> : column_result_t {}; + + template + struct column_result_t, void> { + using type = std::tuple::type>::type...>; + }; + + template + struct column_result_t> : column_result_t {}; + + template + struct column_result_t::value>::type> { + using left_type = typename T::left_type; + using right_type = typename T::right_type; + using left_result = typename column_result_t::type; + using right_result = typename column_result_t::type; + static_assert(std::is_same::value, + "Compound subselect queries must return same types"); + using type = left_result; + }; + + /** + * Result for the most simple queries like `SELECT 1` + */ + template + struct column_result_t::value>::type> { + using type = T; + }; + + /** + * Result for the most simple queries like `SELECT 'ototo'` + */ + template + struct column_result_t { + using type = std::string; + }; + + template + struct column_result_t { + using type = std::string; + }; + + template + struct column_result_t, void> : column_result_t::type, void> {}; + + template + struct column_result_t, void> { + using type = typename storage_traits::storage_mapped_columns::type; + }; + + template + struct column_result_t, void> { + using type = T; + }; + + template + struct column_result_t, void> { + using type = T; + }; + + template + struct column_result_t, void> { + using type = R; + }; + + template + struct column_result_t, void> { + using type = bool; + }; + + template + struct column_result_t, void> { + using type = bool; + }; + + template + struct column_result_t, void> { + using type = bool; + }; + + template + struct column_result_t, void> : column_result_t {}; + } +} +#pragma once + +#include // std::string +#include // std::remove_reference, std::is_same, std::is_base_of +#include // std::vector +#include // std::tuple_size, std::tuple_element +#include // std::reverse, std::find_if + +// #include "column_result.h" + +// #include "static_magic.h" + +// #include "typed_comparator.h" + +// #include "constraints.h" + +// #include "tuple_helper.h" + +// #include "table_info.h" + +// #include "type_printer.h" + +// #include "column.h" + +namespace sqlite_orm { + + namespace internal { + + struct table_base { + + /** + * Table name. + */ + std::string name; + + bool _without_rowid = false; + }; + + /** + * Table interface class. Implementation is hidden in `table_impl` class. + */ + template + struct table_t : table_base { + using object_type = T; + using columns_type = std::tuple; + + static constexpr const int columns_count = static_cast(std::tuple_size::value); + + columns_type columns; + + table_t(decltype(name) name_, columns_type columns_) : + table_base{std::move(name_)}, columns(std::move(columns_)) {} + + table_t without_rowid() const { + auto res = *this; + res._without_rowid = true; + return res; + } + + /** + * Function used to get field value from object by mapped member pointer/setter/getter + */ + template + const F *get_object_field_pointer(const object_type &obj, C c) const { + const F *res = nullptr; + this->for_each_column_with_field_type([&res, &c, &obj](auto &col) { + using column_type = typename std::remove_reference::type; + using member_pointer_t = typename column_type::member_pointer_t; + using getter_type = typename column_type::getter_type; + using setter_type = typename column_type::setter_type; + // Make static_if have at least one input as a workaround for GCC bug: + // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=64095 + if(!res) { + static_if{}>([&res, &obj, &col](const C &c) { + if(compare_any(col.member_pointer, c)) { + res = &(obj.*col.member_pointer); + } + })(c); + } + if(!res) { + static_if{}>([&res, &obj, &col](const C &c) { + if(compare_any(col.getter, c)) { + res = &((obj).*(col.getter))(); + } + })(c); + } + if(!res) { + static_if{}>([&res, &obj, &col](const C &c) { + if(compare_any(col.setter, c)) { + res = &((obj).*(col.getter))(); + } + })(c); + } + }); + return res; + } + + /** + * @return vector of column names of table. + */ + std::vector column_names() const { + std::vector res; + this->for_each_column([&res](auto &c) { + res.push_back(c.name); + }); + return res; + } + + /** + * Calls **l** with every primary key dedicated constraint + */ + template + void for_each_primary_key(const L &l) const { + iterate_tuple(this->columns, [&l](auto &column) { + using column_type = typename std::decay::type; + static_if{}>(l)(column); + }); + } + + std::vector composite_key_columns_names() const { + std::vector res; + this->for_each_primary_key([this, &res](auto &c) { + res = this->composite_key_columns_names(c); + }); + return res; + } + + std::vector primary_key_column_names() const { + std::vector res; + this->for_each_column_with>([&res](auto &c) { + res.push_back(c.name); + }); + if(!res.size()) { + res = this->composite_key_columns_names(); + } + return res; + } + + template + std::vector composite_key_columns_names(const constraints::primary_key_t &pk) const { + std::vector res; + using pk_columns_tuple = decltype(pk.columns); + res.reserve(std::tuple_size::value); + iterate_tuple(pk.columns, [this, &res](auto &v) { + res.push_back(this->find_column_name(v)); + }); + return res; + } + + /** + * Searches column name by class member pointer passed as the first argument. + * @return column name or empty string if nothing found. + */ + template::value && + !std::is_member_function_pointer::value>::type> + std::string find_column_name(F O::*m) const { + std::string res; + this->template for_each_column_with_field_type([&res, m](auto &c) { + if(c.member_pointer == m) { + res = c.name; + } + }); + return res; + } + + /** + * Searches column name by class getter function member pointer passed as first argument. + * @return column name or empty string if nothing found. + */ + template + std::string find_column_name(G getter, + typename std::enable_if::value>::type * = nullptr) const { + std::string res; + using field_type = typename getter_traits::field_type; + this->template for_each_column_with_field_type([&res, getter](auto &c) { + if(compare_any(c.getter, getter)) { + res = c.name; + } + }); + return res; + } + + /** + * Searches column name by class setter function member pointer passed as first argument. + * @return column name or empty string if nothing found. + */ + template + std::string find_column_name(S setter, + typename std::enable_if::value>::type * = nullptr) const { + std::string res; + using field_type = typename setter_traits::field_type; + this->template for_each_column_with_field_type([&res, setter](auto &c) { + if(compare_any(c.setter, setter)) { + res = c.name; + } + }); + return res; + } + + /** + * Iterates all columns and fires passed lambda. Lambda must have one and only templated argument Otherwise + * code will not compile. Excludes table constraints (e.g. foreign_key_t) at the end of the columns list. To + * iterate columns with table constraints use iterate_tuple(columns, ...) instead. L is lambda type. Do + * not specify it explicitly. + * @param l Lambda to be called per column itself. Must have signature like this [] (auto col) -> void {} + */ + template + void for_each_column(const L &l) const { + iterate_tuple(this->columns, [&l](auto &column) { + using column_type = typename std::decay::type; + static_if{}>(l)(column); + }); + } + + template + void for_each_column_with_field_type(const L &l) const { + iterate_tuple(this->columns, [&l](auto &column) { + using column_type = typename std::decay::type; + using field_type = typename column_field_type::type; + static_if{}>(l)(column); + }); + } + + /** + * Iterates all columns that have specified constraints and fires passed lambda. + * Lambda must have one and only templated argument Otherwise code will not compile. + * L is lambda type. Do not specify it explicitly. + * @param l Lambda to be called per column itself. Must have signature like this [] (auto col) -> void {} + */ + template + void for_each_column_with(const L &l) const { + using tuple_helper::tuple_contains_type; + iterate_tuple(this->columns, [&l](auto &column) { + using column_type = typename std::decay::type; + using constraints_type = typename column_constraints_type::type; + static_if{}>(l)(column); + }); + } + + std::vector get_table_info() const { + std::vector res; + res.reserve(size_t(this->columns_count)); + this->for_each_column([&res](auto &col) { + std::string dft; + using field_type = typename std::decay::type::field_type; + if(auto d = col.default_value()) { + dft = *d; + } + table_info i{ + -1, + col.name, + type_printer().print(), + col.not_null(), + dft, + col.template has>(), + }; + res.emplace_back(i); + }); + auto compositeKeyColumnNames = this->composite_key_columns_names(); + for(size_t i = 0; i < compositeKeyColumnNames.size(); ++i) { + auto &columnName = compositeKeyColumnNames[i]; + auto it = std::find_if(res.begin(), res.end(), [&columnName](const table_info &ti) { + return ti.name == columnName; + }); + if(it != res.end()) { + it->pk = static_cast(i + 1); + } + } + return res; + } + }; + } + + /** + * Function used for table creation. Do not use table constructor - use this function + * cause table class is templated and its constructing too (just like std::make_unique or std::make_pair). + */ + template>::type::object_type> + internal::table_t make_table(const std::string &name, Cs... args) { + return {name, std::make_tuple(std::forward(args)...)}; + } + + template + internal::table_t make_table(const std::string &name, Cs... args) { + return {name, std::make_tuple(std::forward(args)...)}; + } +} +#pragma once + +#include // std::string +#include "sqlite3.h" +#include // std::nullptr_t +#include // std::system_error, std::error_code +#include // std::stringstream +#include // std::atoi +#include // std::forward, std::enable_if, std::is_same, std::remove_reference, std::false_type, std::true_type +#include // std::pair, std::make_pair +#include // std::vector +#include // std::find_if +#include // std::type_index + +// #include "error_code.h" + +// #include "statement_finalizer.h" + +// #include "row_extractor.h" + +// #include "constraints.h" + +// #include "select_constraints.h" + +// #include "field_printer.h" + +// #include "table_info.h" + +// #include "sync_schema_result.h" + +// #include "field_value_holder.h" + +#include // std::enable_if + +// #include "column.h" + +namespace sqlite_orm { + namespace internal { + + template + struct field_value_holder; + + template + struct field_value_holder::returns_lvalue>::type> { + using type = typename getter_traits::field_type; + + const type &value; + }; + + template + struct field_value_holder::returns_lvalue>::type> { + using type = typename getter_traits::field_type; + + type value; + }; + } +} + +namespace sqlite_orm { + + namespace internal { + + struct storage_impl_base { + + bool table_exists(const std::string &tableName, sqlite3 *db) const { + auto res = false; + std::stringstream ss; + ss << "SELECT COUNT(*) FROM sqlite_master WHERE type = '" + << "table" + << "' AND name = '" << tableName << "'"; + auto query = ss.str(); + auto rc = sqlite3_exec( + db, + query.c_str(), + [](void *data, int argc, char **argv, char * * /*azColName*/) -> int { + auto &res = *(bool *)data; + if(argc) { + res = !!std::atoi(argv[0]); + } + return 0; + }, + &res, + nullptr); + if(rc != SQLITE_OK) { + throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), + sqlite3_errmsg(db)); + } + return res; + } + + void rename_table(sqlite3 *db, const std::string &oldName, const std::string &newName) const { + std::stringstream ss; + ss << "ALTER TABLE " << oldName << " RENAME TO " << newName; + auto query = ss.str(); + sqlite3_stmt *stmt; + if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { + statement_finalizer finalizer{stmt}; + if(sqlite3_step(stmt) == SQLITE_DONE) { + // done.. + } else { + throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), + sqlite3_errmsg(db)); + } + } else { + throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), + sqlite3_errmsg(db)); + } + } + + static bool get_remove_add_columns(std::vector &columnsToAdd, + std::vector &storageTableInfo, + std::vector &dbTableInfo) { + bool notEqual = false; + + // iterate through storage columns + for(size_t storageColumnInfoIndex = 0; storageColumnInfoIndex < storageTableInfo.size(); + ++storageColumnInfoIndex) { + + // get storage's column info + auto &storageColumnInfo = storageTableInfo[storageColumnInfoIndex]; + auto &columnName = storageColumnInfo.name; + + // search for a column in db eith the same name + auto dbColumnInfoIt = std::find_if(dbTableInfo.begin(), dbTableInfo.end(), [&columnName](auto &ti) { + return ti.name == columnName; + }); + if(dbColumnInfoIt != dbTableInfo.end()) { + auto &dbColumnInfo = *dbColumnInfoIt; + auto columnsAreEqual = + dbColumnInfo.name == storageColumnInfo.name && + dbColumnInfo.notnull == storageColumnInfo.notnull && + (dbColumnInfo.dflt_value.length() > 0) == (storageColumnInfo.dflt_value.length() > 0) && + dbColumnInfo.pk == storageColumnInfo.pk; + if(!columnsAreEqual) { + notEqual = true; + break; + } + dbTableInfo.erase(dbColumnInfoIt); + storageTableInfo.erase(storageTableInfo.begin() + + static_cast(storageColumnInfoIndex)); + --storageColumnInfoIndex; + } else { + columnsToAdd.push_back(&storageColumnInfo); + } + } + return notEqual; + } + + std::vector get_table_info(const std::string &tableName, sqlite3 *db) const { + std::vector res; + auto query = "PRAGMA table_info('" + tableName + "')"; + auto rc = sqlite3_exec( + db, + query.c_str(), + [](void *data, int argc, char **argv, char **) -> int { + auto &res = *(std::vector *)data; + if(argc) { + auto index = 0; + auto cid = std::atoi(argv[index++]); + std::string name = argv[index++]; + std::string type = argv[index++]; + bool notnull = !!std::atoi(argv[index++]); + std::string dflt_value = argv[index] ? argv[index] : ""; + index++; + auto pk = std::atoi(argv[index++]); + res.push_back(table_info{cid, name, type, notnull, dflt_value, pk}); + } + return 0; + }, + &res, + nullptr); + if(rc != SQLITE_OK) { + throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), + sqlite3_errmsg(db)); + } + return res; + } + }; + + /** + * This is a generic implementation. Used as a tail in storage_impl inheritance chain + */ + template + struct storage_impl; + + template + struct storage_impl : public storage_impl { + using table_type = H; + using super = storage_impl; + + storage_impl(H h, Ts... ts) : super(std::forward(ts)...), table(std::move(h)) {} + + table_type table; + + template + void for_each(const L &l) { + this->super::for_each(l); + l(*this); + } + +#if SQLITE_VERSION_NUMBER >= 3006019 + + /** + * Returns foreign keys count in table definition + */ + int foreign_keys_count() { + auto res = 0; + iterate_tuple(this->table.columns, [&res](auto &c) { + if(internal::is_foreign_key::type>::value) { + ++res; + } + }); + return res; + } + +#endif + + /** + * Is used to get column name by member pointer to a base class. + * Main difference between `column_name` and `column_name_simple` is that + * `column_name` has SFINAE check for type equality but `column_name_simple` has not. + */ + template + std::string column_name_simple(F O::*m) const { + return this->table.find_column_name(m); + } + + /** + * Cute function used to find column name by its type and member pointer. Uses SFINAE to + * skip inequal type O. + */ + template + std::string column_name(F O::*m, + typename std::enable_if::value>::type * = nullptr) const { + return this->table.find_column_name(m); + } + + /** + * Opposite version of function defined above. Just calls same function in superclass. + */ + template + std::string column_name(F O::*m, + typename std::enable_if::value>::type * = nullptr) const { + return this->super::column_name(m); + } + + template + std::string column_name(const column_pointer &c, + typename std::enable_if::value>::type * = nullptr) const { + return this->column_name_simple(c.field); + } + + template + std::string column_name(const column_pointer &c, + typename std::enable_if::value>::type * = nullptr) const { + return this->super::column_name(c); + } + + template + const auto &get_impl(typename std::enable_if::value>::type * = nullptr) const { + return *this; + } + + template + const auto &get_impl(typename std::enable_if::value>::type * = nullptr) const { + return this->super::template get_impl(); + } + + template + auto &get_impl(typename std::enable_if::value>::type * = nullptr) { + return *this; + } + + template + auto &get_impl(typename std::enable_if::value>::type * = nullptr) { + return this->super::template get_impl(); + } + + template + const auto *find_table(typename std::enable_if::value>::type * = nullptr) const { + return &this->table; + } + + template + const auto *find_table(typename std::enable_if::value>::type * = nullptr) const { + return this->super::template find_table(); + } + + std::string find_table_name(std::type_index ti) const { + std::type_index thisTypeIndex{typeid(typename H::object_type)}; + if(thisTypeIndex == ti) { + return this->table.name; + } else { + return this->super::find_table_name(ti); + } + } + + void add_column(const table_info &ti, sqlite3 *db) const { + std::stringstream ss; + ss << "ALTER TABLE " << this->table.name << " ADD COLUMN " << ti.name << " "; + ss << ti.type << " "; + if(ti.pk) { + ss << "PRIMARY KEY "; + } + if(ti.notnull) { + ss << "NOT NULL "; + } + if(ti.dflt_value.length()) { + ss << "DEFAULT " << ti.dflt_value << " "; + } + auto query = ss.str(); + sqlite3_stmt *stmt; + auto prepareResult = sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr); + if(prepareResult == SQLITE_OK) { + statement_finalizer finalizer{stmt}; + if(sqlite3_step(stmt) == SQLITE_DONE) { + //.. + } else { + throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), + sqlite3_errmsg(db)); + } + } else { + throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), + sqlite3_errmsg(db)); + } + } + + /** + * Copies current table to another table with a given **name**. + * Performs CREATE TABLE %name% AS SELECT %this->table.columns_names()% FROM &this->table.name%; + */ + void + copy_table(sqlite3 *db, const std::string &name, const std::vector &columnsToIgnore) const { + std::ignore = columnsToIgnore; + + std::stringstream ss; + std::vector columnNames; + this->table.for_each_column([&columnNames, &columnsToIgnore](auto &c) { + auto &columnName = c.name; + auto columnToIgnoreIt = std::find_if(columnsToIgnore.begin(), + columnsToIgnore.end(), + [&columnName](auto tableInfoPointer) { + return columnName == tableInfoPointer->name; + }); + if(columnToIgnoreIt == columnsToIgnore.end()) { + columnNames.emplace_back(columnName); + } + }); + auto columnNamesCount = columnNames.size(); + ss << "INSERT INTO " << name << " ("; + for(size_t i = 0; i < columnNamesCount; ++i) { + ss << columnNames[i]; + if(i < columnNamesCount - 1) { + ss << ","; + } + ss << " "; + } + ss << ") "; + ss << "SELECT "; + for(size_t i = 0; i < columnNamesCount; ++i) { + ss << columnNames[i]; + if(i < columnNamesCount - 1) { + ss << ","; + } + ss << " "; + } + ss << "FROM '" << this->table.name << "' "; + auto query = ss.str(); + sqlite3_stmt *stmt; + if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { + statement_finalizer finalizer{stmt}; + if(sqlite3_step(stmt) == SQLITE_DONE) { + //.. + } else { + throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), + sqlite3_errmsg(db)); + } + } else { + throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), + sqlite3_errmsg(db)); + } + } + + sync_schema_result schema_status(sqlite3 *db, bool preserve) const { + + auto res = sync_schema_result::already_in_sync; + + // first let's see if table with such name exists.. + auto gottaCreateTable = !this->table_exists(this->table.name, db); + if(!gottaCreateTable) { + + // get table info provided in `make_table` call.. + auto storageTableInfo = this->table.get_table_info(); + + // now get current table info from db using `PRAGMA table_info` query.. + auto dbTableInfo = this->get_table_info(this->table.name, db); + + // this vector will contain pointers to columns that gotta be added.. + std::vector columnsToAdd; + + if(this->get_remove_add_columns(columnsToAdd, storageTableInfo, dbTableInfo)) { + gottaCreateTable = true; + } + + if(!gottaCreateTable) { // if all storage columns are equal to actual db columns but there are + // excess columns at the db.. + if(dbTableInfo.size() > 0) { + // extra table columns than storage columns + if(!preserve) { + gottaCreateTable = true; + } else { + res = decltype(res)::old_columns_removed; + } + } + } + if(gottaCreateTable) { + res = decltype(res)::dropped_and_recreated; + } else { + if(columnsToAdd.size()) { + // extra storage columns than table columns + for(auto columnPointer: columnsToAdd) { + if(columnPointer->notnull && columnPointer->dflt_value.empty()) { + gottaCreateTable = true; + break; + } + } + if(!gottaCreateTable) { + if(res == decltype(res)::old_columns_removed) { + res = decltype(res)::new_columns_added_and_old_columns_removed; + } else { + res = decltype(res)::new_columns_added; + } + } else { + res = decltype(res)::dropped_and_recreated; + } + } else { + if(res != decltype(res)::old_columns_removed) { + res = decltype(res)::already_in_sync; + } + } + } + } else { + res = decltype(res)::new_table_created; + } + return res; + } + + private: + using self = storage_impl; + }; + + template<> + struct storage_impl<> : storage_impl_base { + + std::string find_table_name(std::type_index) const { + return {}; + } + + template + void for_each(const L &) {} + + int foreign_keys_count() { + return 0; + } + + template + const void *find_table() const { + return nullptr; + } + }; + + template + struct is_storage_impl : std::false_type {}; + + template + struct is_storage_impl> : std::true_type {}; + } +} +#pragma once + +#include // std::unique/shared_ptr, std::make_unique/shared +#include // std::string +#include "sqlite3.h" +#include // std::remove_reference, std::is_base_of, std::decay, std::false_type, std::true_type +#include // std::ptrdiff_t +#include // std::input_iterator_tag, std::iterator_traits, std::distance +#include // std::function +#include // std::stringstream +#include // std::map +#include // std::vector +#include // std::tuple_size, std::tuple, std::make_tuple +#include // std::forward, std::pair +#include // std::find + +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED +#include // std::optional +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED + +// #include "alias.h" + +// #include "row_extractor_builder.h" + +// #include "row_extractor.h" + +// #include "mapped_row_extractor.h" + +#include "sqlite3.h" + +// #include "object_from_column_builder.h" + +#include "sqlite3.h" + +// #include "row_extractor.h" + +namespace sqlite_orm { + + namespace internal { + + struct object_from_column_builder_base { + sqlite3_stmt *stmt = nullptr; + mutable int index = 0; + }; + + /** + * This is a cute lambda replacement which is used in several places. + */ + template + struct object_from_column_builder : object_from_column_builder_base { + using object_type = O; + + object_type &object; + + object_from_column_builder(object_type &object_, sqlite3_stmt *stmt_) : + object_from_column_builder_base{stmt_}, object(object_) {} + + template + void operator()(const C &c) const { + using field_type = typename C::field_type; + auto value = row_extractor().extract(this->stmt, this->index++); + if(c.member_pointer) { + this->object.*c.member_pointer = std::move(value); + } else { + ((this->object).*(c.setter))(std::move(value)); + } + } + }; + + } +} + +namespace sqlite_orm { + + namespace internal { + + /** + * This is a private row extractor class. It is used for extracting rows as objects instead of tuple. + * Main difference from regular `row_extractor` is that this class takes table info which is required + * for constructing objects by member pointers. To construct please use `row_extractor_builder` class + * Type arguments: + * V is value type just like regular `row_extractor` has + * T is table info class `table_t` + */ + template + struct mapped_row_extractor { + using table_info_t = T; + + mapped_row_extractor(const table_info_t &tableInfo_) : tableInfo(tableInfo_) {} + + V extract(sqlite3_stmt *stmt, int /*columnIndex*/) { + V res; + object_from_column_builder builder{res, stmt}; + this->tableInfo.for_each_column(builder); + return res; + } + + const table_info_t &tableInfo; + }; + + } + +} + +namespace sqlite_orm { + + namespace internal { + + /** + * This builder is used to construct different row extractors depending on type. + * It has two specializations: for mapped to storage types (e.g. User, Visit etc) and + * for non-mapped (e.g. std::string, QString, int etc). For non mapped its operator() returns + * generic `row_extractor`, for mapped it returns `mapped_row_extractor` instance. + */ + template + struct row_extractor_builder; + + template + struct row_extractor_builder { + + row_extractor operator()(const I * /*tableInfo*/) const { + return {}; + } + }; + + template + struct row_extractor_builder { + + mapped_row_extractor operator()(const I *tableInfo) const { + return {*tableInfo}; + } + }; + + template + auto make_row_extractor(const I *tableInfo) { + using builder_t = row_extractor_builder; + return builder_t{}(tableInfo); + } + + } + +} + +// #include "error_code.h" + +// #include "type_printer.h" + +// #include "tuple_helper.h" + +// #include "constraints.h" + +// #include "type_is_nullable.h" + +// #include "field_printer.h" + +// #include "rowid.h" + +// #include "operators.h" + +// #include "select_constraints.h" + +// #include "core_functions.h" + +// #include "conditions.h" + +// #include "statement_binder.h" + +// #include "column_result.h" + +// #include "mapped_type_proxy.h" + +// #include "sync_schema_result.h" + +// #include "table_info.h" + +// #include "storage_impl.h" + +// #include "journal_mode.h" + +// #include "field_value_holder.h" + +// #include "view.h" + +#include // std::shared_ptr +#include // std::string +#include // std::forward, std::move +#include "sqlite3.h" +#include // std::system_error +#include // std::tuple, std::make_tuple + +// #include "row_extractor.h" + +// #include "statement_finalizer.h" + +// #include "error_code.h" + +// #include "iterator.h" + +#include // std::shared_ptr, std::unique_ptr, std::make_shared +#include "sqlite3.h" +#include // std::decay +#include // std::move +#include // std::ptrdiff_t +#include // std::input_iterator_tag +#include // std::system_error +#include // std::make_error_code + +// #include "row_extractor.h" + +// #include "statement_finalizer.h" + +// #include "error_code.h" + +// #include "object_from_column_builder.h" + +namespace sqlite_orm { + + namespace internal { + + template + struct iterator_t { + using view_type = V; + using value_type = typename view_type::mapped_type; + + protected: + /** + * The double-indirection is so that copies of the iterator + * share the same sqlite3_stmt from a sqlite3_prepare_v2() + * call. When one finishes iterating it the pointer + * inside the shared_ptr is nulled out in all copies. + */ + std::shared_ptr stmt; + view_type &view; + + /** + * shared_ptr is used over unique_ptr here + * so that the iterator can be copyable. + */ + std::shared_ptr current; + + void extract_value(std::unique_ptr &temp) { + temp = std::make_unique(); + auto &storage = this->view.storage; + auto &impl = storage.template get_impl(); + object_from_column_builder builder{*temp, *this->stmt}; + impl.table.for_each_column(builder); + } + + public: + using difference_type = std::ptrdiff_t; + using pointer = value_type *; + using reference = value_type &; + using iterator_category = std::input_iterator_tag; + + iterator_t(sqlite3_stmt *stmt_, view_type &view_) : + stmt(std::make_shared(stmt_)), view(view_) { + this->operator++(); + } + + iterator_t(const iterator_t &) = default; + + iterator_t(iterator_t &&) = default; + + iterator_t &operator=(iterator_t &&) = default; + + iterator_t &operator=(const iterator_t &) = default; + + ~iterator_t() { + if(this->stmt) { + statement_finalizer f{*this->stmt}; + } + } + + value_type &operator*() { + if(!this->stmt) { + throw std::system_error(std::make_error_code(orm_error_code::trying_to_dereference_null_iterator)); + } + if(!this->current) { + std::unique_ptr value; + this->extract_value(value); + this->current = move(value); + } + return *this->current; + } + + value_type *operator->() { + return &(this->operator*()); + } + + void operator++() { + if(this->stmt && *this->stmt) { + auto ret = sqlite3_step(*this->stmt); + switch(ret) { + case SQLITE_ROW: + this->current = nullptr; + break; + case SQLITE_DONE: { + statement_finalizer f{*this->stmt}; + *this->stmt = nullptr; + } break; + default: { + auto db = this->view.connection.get(); + throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), + sqlite3_errmsg(db)); + } + } + } + } + + void operator++(int) { + this->operator++(); + } + + bool operator==(const iterator_t &other) const { + if(this->stmt && other.stmt) { + return *this->stmt == *other.stmt; + } else { + if(!this->stmt && !other.stmt) { + return true; + } else { + return false; + } + } + } + + bool operator!=(const iterator_t &other) const { + return !(*this == other); + } + }; + } +} + +// #include "ast_iterator.h" + +#include // std::vector +#include // std::reference_wrapper + +// #include "conditions.h" + +// #include "select_constraints.h" + +// #include "operators.h" + +// #include "tuple_helper.h" + +// #include "core_functions.h" + +// #include "prepared_statement.h" + +#include "sqlite3.h" +#include // std::iterator_traits +#include // std::string +#include // std::true_type, std::false_type +#include // std::pair + +// #include "connection_holder.h" + +#include "sqlite3.h" +#include // std::string +#include // std::system_error + +// #include "error_code.h" + +namespace sqlite_orm { + + namespace internal { + + struct connection_holder { + + connection_holder(std::string filename_) : filename(move(filename_)) {} + + void retain() { + ++this->_retain_count; + if(1 == this->_retain_count) { + auto rc = sqlite3_open(this->filename.c_str(), &this->db); + if(rc != SQLITE_OK) { + throw std::system_error(std::error_code(sqlite3_errcode(this->db), get_sqlite_error_category()), + sqlite3_errmsg(this->db)); + } + } + } + + void release() { + --this->_retain_count; + if(0 == this->_retain_count) { + auto rc = sqlite3_close(this->db); + if(rc != SQLITE_OK) { + throw std::system_error(std::error_code(sqlite3_errcode(this->db), get_sqlite_error_category()), + sqlite3_errmsg(this->db)); + } + } + } + + sqlite3 *get() const { + return this->db; + } + + int retain_count() const { + return this->_retain_count; + } + + const std::string filename; + + protected: + sqlite3 *db = nullptr; + int _retain_count = 0; + }; + + struct connection_ref { + connection_ref(connection_holder &holder_) : holder(holder_) { + this->holder.retain(); + } + + connection_ref(const connection_ref &other) : holder(other.holder) { + this->holder.retain(); + } + + connection_ref(connection_ref &&other) : holder(other.holder) { + this->holder.retain(); + } + + ~connection_ref() { + this->holder.release(); + } + + sqlite3 *get() const { + return this->holder.get(); + } + + protected: + connection_holder &holder; + }; + } +} + +// #include "select_constraints.h" + +namespace sqlite_orm { + + namespace internal { + + struct prepared_statement_base { + sqlite3_stmt *stmt = nullptr; + connection_ref con; + + ~prepared_statement_base() { + if(this->stmt) { + sqlite3_finalize(this->stmt); + this->stmt = nullptr; + } + } + + std::string sql() const { + if(this->stmt) { + if(auto res = sqlite3_sql(this->stmt)) { + return res; + } else { + return {}; + } + } else { + return {}; + } + } + +#if SQLITE_VERSION_NUMBER >= 3014000 + std::string expanded_sql() const { + if(this->stmt) { + if(auto res = sqlite3_expanded_sql(this->stmt)) { + std::string result = res; + sqlite3_free(res); + return result; + } else { + return {}; + } + } else { + return {}; + } + } +#endif +#if SQLITE_VERSION_NUMBER >= 3026000 and defined(SQLITE_ENABLE_NORMALIZE) + std::string normalized_sql() const { + if(this->stmt) { + if(auto res = sqlite3_normalized_sql(this->stmt)) { + return res; + } else { + return {}; + } + } else { + return {}; + } + } +#endif + }; + + template + struct prepared_statement_t : prepared_statement_base { + using expression_type = T; + + expression_type t; + + prepared_statement_t(T t_, sqlite3_stmt *stmt, connection_ref con_) : + prepared_statement_base{stmt, std::move(con_)}, t(std::move(t_)) {} + }; + + template + struct is_prepared_statement : std::false_type {}; + + template + struct is_prepared_statement> : std::true_type {}; + + /** + * T - type of object to obtain from a database + */ + template + struct get_all_t { + using type = T; + using return_type = R; + + using conditions_type = std::tuple; + + conditions_type conditions; + }; + + template + struct get_all_pointer_t { + using type = T; + using return_type = R; + + using conditions_type = std::tuple; + + conditions_type conditions; + }; + +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + template + struct get_all_optional_t { + using type = T; + using return_type = R; + + using conditions_type = std::tuple; + + conditions_type conditions; + }; +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED + + template + struct update_all_t; + + template + struct update_all_t, Wargs...> { + using set_type = set_t; + using conditions_type = std::tuple; + + set_type set; + conditions_type conditions; + }; + + template + struct remove_all_t { + using type = T; + using conditions_type = std::tuple; + + conditions_type conditions; + }; + + template + struct get_t { + using type = T; + using ids_type = std::tuple; + + ids_type ids; + }; + + template + struct get_pointer_t { + using type = T; + using ids_type = std::tuple; + + ids_type ids; + }; + +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + template + struct get_optional_t { + using type = T; + using ids_type = std::tuple; + + ids_type ids; + }; +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED + + template + struct update_t { + using type = T; + + type obj; + }; + + template + struct remove_t { + using type = T; + using ids_type = std::tuple; + + ids_type ids; + }; + + template + struct insert_t { + using type = T; + + type obj; + }; + + template + struct insert_explicit { + using type = T; + using columns_type = columns_t; + + type obj; + columns_type columns; + }; + + template + struct replace_t { + using type = T; + + type obj; + }; + + template + struct insert_range_t { + using iterator_type = It; + using object_type = typename std::iterator_traits::value_type; + + std::pair range; + }; + + template + struct replace_range_t { + using iterator_type = It; + using object_type = typename std::iterator_traits::value_type; + + std::pair range; + }; + } + + /** + * Create a replace range statement + */ + template + internal::replace_range_t replace_range(It from, It to) { + return {{std::move(from), std::move(to)}}; + } + + /** + * Create an insert range statement + */ + template + internal::insert_range_t insert_range(It from, It to) { + return {{std::move(from), std::move(to)}}; + } + + /** + * Create a replace statement. + * T is an object type mapped to a storage. + * Usage: storage.replace(myUserInstance); + * Parameter obj is accepted by value. If you want to accept it by ref + * please use std::ref function: storage.replace(std::ref(myUserInstance)); + */ + template + internal::replace_t replace(T obj) { + return {std::move(obj)}; + } + + /** + * Create an insert statement. + * T is an object type mapped to a storage. + * Usage: storage.insert(myUserInstance); + * Parameter obj is accepted by value. If you want to accept it by ref + * please use std::ref function: storage.insert(std::ref(myUserInstance)); + */ + template + internal::insert_t insert(T obj) { + return {std::move(obj)}; + } + + /** + * Create an explicit insert statement. + * T is an object type mapped to a storage. + * Cols is columns types aparameter pack. Must contain member pointers + * Usage: storage.insert(myUserInstance, columns(&User::id, &User::name)); + * Parameter obj is accepted by value. If you want to accept it by ref + * please use std::ref function: storage.insert(std::ref(myUserInstance), columns(&User::id, &User::name)); + */ + template + internal::insert_explicit insert(T obj, internal::columns_t cols) { + return {std::move(obj), std::move(cols)}; + } + + /** + * Create a remove statement + * T is an object type mapped to a storage. + * Usage: remove(5); + */ + template + internal::remove_t remove(Ids... ids) { + std::tuple idsTuple{std::forward(ids)...}; + return {move(idsTuple)}; + } + + /** + * Create an update statement. + * T is an object type mapped to a storage. + * Usage: storage.update(myUserInstance); + * Parameter obj is accepted by value. If you want to accept it by ref + * please use std::ref function: storage.update(std::ref(myUserInstance)); + */ + template + internal::update_t update(T obj) { + return {std::move(obj)}; + } + + /** + * Create a get statement. + * T is an object type mapped to a storage. + * Usage: get(5); + */ + template + internal::get_t get(Ids... ids) { + std::tuple idsTuple{std::forward(ids)...}; + return {move(idsTuple)}; + } + + /** + * Create a get pointer statement. + * T is an object type mapped to a storage. + * Usage: get_pointer(5); + */ + template + internal::get_pointer_t get_pointer(Ids... ids) { + std::tuple idsTuple{std::forward(ids)...}; + return {move(idsTuple)}; + } + +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + /** + * Create a get optional statement. + * T is an object type mapped to a storage. + * Usage: get_optional(5); + */ + template + internal::get_optional_t get_optional(Ids... ids) { + std::tuple idsTuple{std::forward(ids)...}; + return {move(idsTuple)}; + } +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED + + /** + * Create a remove all statement. + * T is an object type mapped to a storage. + * Usage: storage.remove_all(...); + */ + template + internal::remove_all_t remove_all(Args... args) { + using args_tuple = std::tuple; + internal::validate_conditions(); + args_tuple conditions{std::forward(args)...}; + return {move(conditions)}; + } + + /** + * Create a get all statement. + * T is an object type mapped to a storage. + * Usage: storage.get_all(...); + */ + template + internal::get_all_t, Args...> get_all(Args... args) { + using args_tuple = std::tuple; + internal::validate_conditions(); + args_tuple conditions{std::forward(args)...}; + return {move(conditions)}; + } + + /** + * Create a get all statement. + * T is an object type mapped to a storage. + * R is a container type. std::vector is default + * Usage: storage.get_all(...); + */ + template + internal::get_all_t get_all(Args... args) { + using args_tuple = std::tuple; + internal::validate_conditions(); + args_tuple conditions{std::forward(args)...}; + return {move(conditions)}; + } + + /** + * Create an update all statement. + * Usage: storage.update_all(set(...), ...); + */ + template + internal::update_all_t, Wargs...> update_all(internal::set_t set, Wargs... wh) { + using args_tuple = std::tuple; + internal::validate_conditions(); + args_tuple conditions{std::forward(wh)...}; + return {std::move(set), move(conditions)}; + } + + /** + * Create a get all pointer statement. + * T is an object type mapped to a storage. + * Usage: storage.get_all_pointer(...); + */ + template + internal::get_all_pointer_t>, Args...> get_all_pointer(Args... args) { + using args_tuple = std::tuple; + internal::validate_conditions(); + args_tuple conditions{std::forward(args)...}; + return {move(conditions)}; + } + /** + * Create a get all pointer statement. + * T is an object type mapped to a storage. + * R is a container return type. std::vector> is default + * Usage: storage.get_all_pointer(...); + */ + template + internal::get_all_pointer_t get_all_pointer(Args... args) { + using args_tuple = std::tuple; + internal::validate_conditions(); + args_tuple conditions{std::forward(args)...}; + return {move(conditions)}; + } + +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + /** + * Create a get all optional statement. + * T is an object type mapped to a storage. + * Usage: storage.get_all_optional(...); + */ + template + internal::get_all_optional_t>, Args...> get_all_optional(Args... args) { + using args_tuple = std::tuple; + internal::validate_conditions(); + args_tuple conditions{std::forward(args)...}; + return {move(conditions)}; + } + + /** + * Create a get all optional statement. + * T is an object type mapped to a storage. + * R is a container return type. std::vector> is default + * Usage: storage.get_all_optional(...); + */ + template + internal::get_all_optional_t get_all_optional(Args... args) { + using args_tuple = std::tuple; + internal::validate_conditions(); + args_tuple conditions{std::forward(args)...}; + return {move(conditions)}; + } +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED +} + +// #include "values.h" + +#include // std::vector +#include +#include // std::tuple + +namespace sqlite_orm { + + namespace internal { + + template + struct values_t { + std::tuple tuple; + }; + + template + struct dynamic_values_t { + std::vector vector; + }; + + } + + template + internal::values_t values(Args... args) { + return {{std::forward(args)...}}; + } + + template + internal::dynamic_values_t values(std::vector vector) { + return {{move(vector)}}; + } + +} + +namespace sqlite_orm { + + namespace internal { + + /** + * ast_iterator accepts any expression and a callable object + * which will be called for any node of provided expression. + * E.g. if we pass `where(is_equal(5, max(&User::id, 10))` then + * callable object will be called with 5, &User::id and 10. + * ast_iterator is used mostly in finding literals to be bound to + * a statement. To use it just call `iterate_ast(object, callable);` + * T is an ast element. E.g. where_t + */ + template + struct ast_iterator { + using node_type = T; + + /** + * L is a callable type. Mostly is a templated lambda + */ + template + void operator()(const T &t, const L &l) const { + l(t); + } + }; + + /** + * Simplified API + */ + template + void iterate_ast(const T &t, const L &l) { + ast_iterator iterator; + iterator(t, l); + } + + template + struct ast_iterator, void> { + using node_type = std::reference_wrapper; + + template + void operator()(const node_type &r, const L &l) const { + iterate_ast(r.get(), l); + } + }; + + template + struct ast_iterator, void> { + using node_type = where_t; + + template + void operator()(const node_type &where, const L &l) const { + iterate_ast(where.c, l); + } + }; + + template + struct ast_iterator::value>::type> { + using node_type = T; + + template + void operator()(const node_type &binaryCondition, const L &l) const { + iterate_ast(binaryCondition.l, l); + iterate_ast(binaryCondition.r, l); + } + }; + + template + struct ast_iterator, void> { + using node_type = binary_operator; + + template + void operator()(const node_type &binaryOperator, const C &l) const { + iterate_ast(binaryOperator.lhs, l); + iterate_ast(binaryOperator.rhs, l); + } + }; + + template + struct ast_iterator, void> { + using node_type = columns_t; + + template + void operator()(const node_type &cols, const L &l) const { + iterate_ast(cols.columns, l); + } + }; + + template + struct ast_iterator, void> { + using node_type = in_t; + + template + void operator()(const node_type &in, const C &l) const { + iterate_ast(in.l, l); + iterate_ast(in.arg, l); + } + }; + + template + struct ast_iterator, void> { + using node_type = std::vector; + + template + void operator()(const node_type &vec, const L &l) const { + for(auto &i: vec) { + iterate_ast(i, l); + } + } + }; + + template<> + struct ast_iterator, void> { + using node_type = std::vector; + + template + void operator()(const node_type &vec, const L &l) const { + l(vec); + } + }; + + template + struct ast_iterator::value>::type> { + using node_type = T; + + template + void operator()(const node_type &c, const L &l) const { + iterate_ast(c.left, l); + iterate_ast(c.right, l); + } + }; + + template + struct ast_iterator, void> { + using node_type = select_t; + + template + void operator()(const node_type &sel, const L &l) const { + iterate_ast(sel.col, l); + iterate_ast(sel.conditions, l); + } + }; + + template + struct ast_iterator, void> { + using node_type = get_all_t; + + template + void operator()(const node_type &get, const L &l) const { + iterate_ast(get.conditions, l); + } + }; + + template + struct ast_iterator, void> { + using node_type = get_all_pointer_t; + + template + void operator()(const node_type &get, const L &l) const { + iterate_ast(get.conditions, l); + } + }; + +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + template + struct ast_iterator, void> { + using node_type = get_all_optional_t; + + template + void operator()(const node_type &get, const L &l) const { + iterate_ast(get.conditions, l); + } + }; +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED + + template + struct ast_iterator, Wargs...>, void> { + using node_type = update_all_t, Wargs...>; + + template + void operator()(const node_type &u, const L &l) const { + iterate_ast(u.set, l); + iterate_ast(u.conditions, l); + } + }; + + template + struct ast_iterator, void> { + using node_type = remove_all_t; + + template + void operator()(const node_type &r, const L &l) const { + iterate_ast(r.conditions, l); + } + }; + + template + struct ast_iterator, void> { + using node_type = set_t; + + template + void operator()(const node_type &s, const L &l) const { + iterate_ast(s.assigns, l); + } + }; + + template + struct ast_iterator, void> { + using node_type = std::tuple; + + template + void operator()(const node_type &tuple, const L &l) const { + iterate_tuple(tuple, [&l](auto &v) { + iterate_ast(v, l); + }); + } + }; + + template + struct ast_iterator, void> { + using node_type = having_t; + + template + void operator()(const node_type &hav, const L &l) const { + iterate_ast(hav.t, l); + } + }; + + template + struct ast_iterator, void> { + using node_type = cast_t; + + template + void operator()(const node_type &c, const L &l) const { + iterate_ast(c.expression, l); + } + }; + + template + struct ast_iterator, void> { + using node_type = exists_t; + + template + void operator()(const node_type &e, const L &l) const { + iterate_ast(e.t, l); + } + }; + + template + struct ast_iterator, void> { + using node_type = like_t; + + template + void operator()(const node_type &lk, const L &l) const { + iterate_ast(lk.arg, l); + iterate_ast(lk.pattern, l); + lk.arg3.apply([&l](auto &value) { + iterate_ast(value, l); + }); + } + }; + + template + struct ast_iterator, void> { + using node_type = glob_t; + + template + void operator()(const node_type &lk, const L &l) const { + iterate_ast(lk.arg, l); + iterate_ast(lk.pattern, l); + } + }; + + template + struct ast_iterator, void> { + using node_type = between_t; + + template + void operator()(const node_type &b, const L &l) const { + iterate_ast(b.expr, l); + iterate_ast(b.b1, l); + iterate_ast(b.b2, l); + } + }; + + template + struct ast_iterator, void> { + using node_type = named_collate; + + template + void operator()(const node_type &col, const L &l) const { + iterate_ast(col.expr, l); + } + }; + + template + struct ast_iterator, void> { + using node_type = negated_condition_t; + + template + void operator()(const node_type &neg, const L &l) const { + iterate_ast(neg.c, l); + } + }; + + template + struct ast_iterator, void> { + using node_type = is_null_t; + + template + void operator()(const node_type &i, const L &l) const { + iterate_ast(i.t, l); + } + }; + + template + struct ast_iterator, void> { + using node_type = is_not_null_t; + + template + void operator()(const node_type &i, const L &l) const { + iterate_ast(i.t, l); + } + }; + + template + struct ast_iterator, void> { + using node_type = core_function_t; + + template + void operator()(const node_type &f, const L &l) const { + iterate_ast(f.args, l); + } + }; + + template + struct ast_iterator, void> { + using node_type = left_join_t; + + template + void operator()(const node_type &j, const L &l) const { + iterate_ast(j.constraint, l); + } + }; + + template + struct ast_iterator, void> { + using node_type = on_t; + + template + void operator()(const node_type &o, const L &l) const { + iterate_ast(o.arg, l); + } + }; + + template + struct ast_iterator, void> { + using node_type = join_t; + + template + void operator()(const node_type &j, const L &l) const { + iterate_ast(j.constraint, l); + } + }; + + template + struct ast_iterator, void> { + using node_type = left_outer_join_t; + + template + void operator()(const node_type &j, const L &l) const { + iterate_ast(j.constraint, l); + } + }; + + template + struct ast_iterator, void> { + using node_type = inner_join_t; + + template + void operator()(const node_type &j, const L &l) const { + iterate_ast(j.constraint, l); + } + }; + + template + struct ast_iterator, void> { + using node_type = simple_case_t; + + template + void operator()(const node_type &c, const L &l) const { + c.case_expression.apply([&l](auto &c) { + iterate_ast(c, l); + }); + iterate_tuple(c.args, [&l](auto &pair) { + iterate_ast(pair.first, l); + iterate_ast(pair.second, l); + }); + c.else_expression.apply([&l](auto &el) { + iterate_ast(el, l); + }); + } + }; + + template + struct ast_iterator, void> { + using node_type = as_t; + + template + void operator()(const node_type &a, const L &l) const { + iterate_ast(a.expression, l); + } + }; + + template + struct ast_iterator, void> { + using node_type = limit_t; + + template + void operator()(const node_type &a, const L &l) const { + iterate_ast(a.lim, l); + } + }; + + template + struct ast_iterator, void> { + using node_type = limit_t; + + template + void operator()(const node_type &a, const L &l) const { + iterate_ast(a.lim, l); + a.off.apply([&l](auto &value) { + iterate_ast(value, l); + }); + } + }; + + template + struct ast_iterator, void> { + using node_type = limit_t; + + template + void operator()(const node_type &a, const L &l) const { + a.off.apply([&l](auto &value) { + iterate_ast(value, l); + }); + iterate_ast(a.lim, l); + } + }; + + template + struct ast_iterator, void> { + using node_type = distinct_t; + + template + void operator()(const node_type &a, const L &l) const { + iterate_ast(a.t, l); + } + }; + + template + struct ast_iterator, void> { + using node_type = all_t; + + template + void operator()(const node_type &a, const L &l) const { + iterate_ast(a.t, l); + } + }; + + template + struct ast_iterator, void> { + using node_type = bitwise_not_t; + + template + void operator()(const node_type &a, const L &l) const { + iterate_ast(a.argument, l); + } + }; + + template + struct ast_iterator, void> { + using node_type = values_t; + + template + void operator()(const node_type &node, const L &l) const { + iterate_ast(node.tuple, l); + } + }; + + template + struct ast_iterator, void> { + using node_type = dynamic_values_t; + + template + void operator()(const node_type &node, const L &l) const { + iterate_ast(node.vector, l); + } + }; + + } +} + +// #include "prepared_statement.h" + +// #include "connection_holder.h" + +namespace sqlite_orm { + + namespace internal { + + template + struct view_t { + using mapped_type = T; + using storage_type = S; + using self = view_t; + + storage_type &storage; + connection_ref connection; + get_all_t, Args...> args; + + view_t(storage_type &stor, decltype(connection) conn, Args &&... args_) : + storage(stor), connection(std::move(conn)), args{std::make_tuple(std::forward(args_)...)} {} + + size_t size() { + return this->storage.template count(); + } + + bool empty() { + return !this->size(); + } + + iterator_t end() { + return {nullptr, *this}; + } + + iterator_t begin() { + sqlite3_stmt *stmt = nullptr; + auto db = this->connection.get(); + using context_t = serializator_context; + context_t context{this->storage.impl}; + context.skip_table_name = false; + context.replace_bindable_with_question = true; + auto query = serialize(this->args, context); + auto ret = sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr); + if(ret == SQLITE_OK) { + auto index = 1; + iterate_ast(this->args.conditions, [&index, stmt, db](auto &node) { + using node_type = typename std::decay::type; + conditional_binder> binder{stmt, index}; + if(SQLITE_OK != binder(node)) { + throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), + sqlite3_errmsg(db)); + } + }); + return {stmt, *this}; + } else { + throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), + sqlite3_errmsg(db)); + } + } + }; + } +} + +// #include "ast_iterator.h" + +// #include "storage_base.h" + +#include // std::function, std::bind +#include "sqlite3.h" +#include // std::string +#include // std::stringstream +#include // std::move +#include // std::system_error, std::error_code, std::make_error_code +#include // std::vector +#include // std::make_shared, std::shared_ptr +#include // std::map +#include // std::decay, std::is_same +#include // std::iter_swap + +// #include "pragma.h" + +#include // std::string +#include "sqlite3.h" +#include // std::function +#include // std::shared_ptr + +// #include "error_code.h" + +// #include "row_extractor.h" + +// #include "journal_mode.h" + +// #include "connection_holder.h" + +namespace sqlite_orm { + + namespace internal { + struct storage_base; + } + + struct pragma_t { + using get_connection_t = std::function; + + pragma_t(get_connection_t get_connection_) : get_connection(std::move(get_connection_)) {} + + sqlite_orm::journal_mode journal_mode() { + return this->get_pragma("journal_mode"); + } + + void journal_mode(sqlite_orm::journal_mode value) { + this->_journal_mode = -1; + this->set_pragma("journal_mode", value); + this->_journal_mode = static_cast_journal_mode)>(value); + } + + int synchronous() { + return this->get_pragma("synchronous"); + } + + void synchronous(int value) { + this->_synchronous = -1; + this->set_pragma("synchronous", value); + this->_synchronous = value; + } + + int user_version() { + return this->get_pragma("user_version"); + } + + void user_version(int value) { + this->set_pragma("user_version", value); + } + + int auto_vacuum() { + return this->get_pragma("auto_vacuum"); + } + + void auto_vacuum(int value) { + this->set_pragma("auto_vacuum", value); + } + + protected: + friend struct storage_base; + + public: + int _synchronous = -1; + signed char _journal_mode = -1; // if != -1 stores static_cast(journal_mode) + get_connection_t get_connection; + + template + T get_pragma(const std::string &name) { + auto connection = this->get_connection(); + auto query = "PRAGMA " + name; + T res; + auto db = connection.get(); + auto rc = sqlite3_exec( + db, + query.c_str(), + [](void *data, int argc, char **argv, char **) -> int { + auto &res = *(T *)data; + if(argc) { + res = row_extractor().extract(argv[0]); + } + return 0; + }, + &res, + nullptr); + if(rc == SQLITE_OK) { + return res; + } else { + throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), + sqlite3_errmsg(db)); + } + } + + /** + * Yevgeniy Zakharov: I wanted to refactore this function with statements and value bindings + * but it turns out that bindings in pragma statements are not supported. + */ + template + void set_pragma(const std::string &name, const T &value, sqlite3 *db = nullptr) { + auto con = this->get_connection(); + if(!db) { + db = con.get(); + } + std::stringstream ss; + ss << "PRAGMA " << name << " = " << value; + auto query = ss.str(); + auto rc = sqlite3_exec(db, query.c_str(), nullptr, nullptr, nullptr); + if(rc != SQLITE_OK) { + throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), + sqlite3_errmsg(db)); + } + } + + void set_pragma(const std::string &name, const sqlite_orm::journal_mode &value, sqlite3 *db = nullptr) { + auto con = this->get_connection(); + if(!db) { + db = con.get(); + } + std::stringstream ss; + ss << "PRAGMA " << name << " = " << internal::to_string(value); + auto query = ss.str(); + auto rc = sqlite3_exec(db, query.c_str(), nullptr, nullptr, nullptr); + if(rc != SQLITE_OK) { + throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), + sqlite3_errmsg(db)); + } + } + }; +} + +// #include "limit_accesor.h" + +#include "sqlite3.h" +#include // std::map +#include // std::function +#include // std::shared_ptr + +// #include "connection_holder.h" + +namespace sqlite_orm { + + namespace internal { + + struct limit_accesor { + using get_connection_t = std::function; + + limit_accesor(get_connection_t get_connection_) : get_connection(std::move(get_connection_)) {} + + int length() { + return this->get(SQLITE_LIMIT_LENGTH); + } + + void length(int newValue) { + this->set(SQLITE_LIMIT_LENGTH, newValue); + } + + int sql_length() { + return this->get(SQLITE_LIMIT_SQL_LENGTH); + } + + void sql_length(int newValue) { + this->set(SQLITE_LIMIT_SQL_LENGTH, newValue); + } + + int column() { + return this->get(SQLITE_LIMIT_COLUMN); + } + + void column(int newValue) { + this->set(SQLITE_LIMIT_COLUMN, newValue); + } + + int expr_depth() { + return this->get(SQLITE_LIMIT_EXPR_DEPTH); + } + + void expr_depth(int newValue) { + this->set(SQLITE_LIMIT_EXPR_DEPTH, newValue); + } + + int compound_select() { + return this->get(SQLITE_LIMIT_COMPOUND_SELECT); + } + + void compound_select(int newValue) { + this->set(SQLITE_LIMIT_COMPOUND_SELECT, newValue); + } + + int vdbe_op() { + return this->get(SQLITE_LIMIT_VDBE_OP); + } + + void vdbe_op(int newValue) { + this->set(SQLITE_LIMIT_VDBE_OP, newValue); + } + + int function_arg() { + return this->get(SQLITE_LIMIT_FUNCTION_ARG); + } + + void function_arg(int newValue) { + this->set(SQLITE_LIMIT_FUNCTION_ARG, newValue); + } + + int attached() { + return this->get(SQLITE_LIMIT_ATTACHED); + } + + void attached(int newValue) { + this->set(SQLITE_LIMIT_ATTACHED, newValue); + } + + int like_pattern_length() { + return this->get(SQLITE_LIMIT_LIKE_PATTERN_LENGTH); + } + + void like_pattern_length(int newValue) { + this->set(SQLITE_LIMIT_LIKE_PATTERN_LENGTH, newValue); + } + + int variable_number() { + return this->get(SQLITE_LIMIT_VARIABLE_NUMBER); + } + + void variable_number(int newValue) { + this->set(SQLITE_LIMIT_VARIABLE_NUMBER, newValue); + } + + int trigger_depth() { + return this->get(SQLITE_LIMIT_TRIGGER_DEPTH); + } + + void trigger_depth(int newValue) { + this->set(SQLITE_LIMIT_TRIGGER_DEPTH, newValue); + } + +#if SQLITE_VERSION_NUMBER >= 3008007 + int worker_threads() { + return this->get(SQLITE_LIMIT_WORKER_THREADS); + } + + void worker_threads(int newValue) { + this->set(SQLITE_LIMIT_WORKER_THREADS, newValue); + } +#endif + + protected: + get_connection_t get_connection; + + friend struct storage_base; + + /** + * Stores limit set between connections. + */ + std::map limits; + + int get(int id) { + auto connection = this->get_connection(); + return sqlite3_limit(connection.get(), id, -1); + } + + void set(int id, int newValue) { + this->limits[id] = newValue; + auto connection = this->get_connection(); + sqlite3_limit(connection.get(), id, newValue); + } + }; + } +} + +// #include "transaction_guard.h" + +#include // std::function + +// #include "connection_holder.h" + +namespace sqlite_orm { + + namespace internal { + + /** + * Class used as a guard for a transaction. Calls `ROLLBACK` in destructor. + * Has explicit `commit()` and `rollback()` functions. After explicit function is fired + * guard won't do anything in d-tor. Also you can set `commit_on_destroy` to true to + * make it call `COMMIT` on destroy. + */ + struct transaction_guard_t { + /** + * This is a public lever to tell a guard what it must do in its destructor + * if `gotta_fire` is true + */ + bool commit_on_destroy = false; + + transaction_guard_t(connection_ref connection_, + std::function commit_func_, + std::function rollback_func_) : + connection(std::move(connection_)), + commit_func(std::move(commit_func_)), rollback_func(std::move(rollback_func_)) {} + + ~transaction_guard_t() { + if(this->gotta_fire) { + if(!this->commit_on_destroy) { + this->rollback_func(); + } else { + this->commit_func(); + } + } + } + + /** + * Call `COMMIT` explicitly. After this call + * guard will not call `COMMIT` or `ROLLBACK` + * in its destructor. + */ + void commit() { + this->commit_func(); + this->gotta_fire = false; + } + + /** + * Call `ROLLBACK` explicitly. After this call + * guard will not call `COMMIT` or `ROLLBACK` + * in its destructor. + */ + void rollback() { + this->rollback_func(); + this->gotta_fire = false; + } + + protected: + connection_ref connection; + std::function commit_func; + std::function rollback_func; + bool gotta_fire = true; + }; + } +} + +// #include "statement_finalizer.h" + +// #include "type_printer.h" + +// #include "tuple_helper.h" + +// #include "row_extractor.h" + +// #include "connection_holder.h" + +// #include "backup.h" + +#include "sqlite3.h" +#include // std::string +#include + +// #include "error_code.h" + +// #include "connection_holder.h" + +namespace sqlite_orm { + + namespace internal { + + /** + * A backup class. Don't construct it as is, call storage.make_backup_from or storage.make_backup_to instead. + * An instance of this class represents a wrapper around sqlite3_backup pointer. Use this class + * to have maximum control on a backup operation. In case you need a single backup in one line you + * can skip creating a backup_t instance and just call storage.backup_from or storage.backup_to function. + */ + struct backup_t { + backup_t(connection_ref to_, + const std::string &zDestName, + connection_ref from_, + const std::string &zSourceName, + std::unique_ptr holder_) : + handle(sqlite3_backup_init(to_.get(), zDestName.c_str(), from_.get(), zSourceName.c_str())), + to(to_), from(from_), holder(move(holder_)) { + if(!this->handle) { + throw std::system_error(std::make_error_code(orm_error_code::failed_to_init_a_backup)); + } + } + + backup_t(backup_t &&other) : + handle(other.handle), to(other.to), from(other.from), holder(move(other.holder)) { + other.handle = nullptr; + } + + ~backup_t() { + if(this->handle) { + (void)sqlite3_backup_finish(this->handle); + this->handle = nullptr; + } + } + + /** + * Calls sqlite3_backup_step with pages argument + */ + int step(int pages) { + return sqlite3_backup_step(this->handle, pages); + } + + /** + * Returns sqlite3_backup_remaining result + */ + int remaining() const { + return sqlite3_backup_remaining(this->handle); + } + + /** + * Returns sqlite3_backup_pagecount result + */ + int pagecount() const { + return sqlite3_backup_pagecount(this->handle); + } + + protected: + sqlite3_backup *handle = nullptr; + connection_ref to; + connection_ref from; + std::unique_ptr holder; + }; + } +} + +namespace sqlite_orm { + + namespace internal { + + struct storage_base { + using collating_function = std::function; + + std::function on_open; + pragma_t pragma; + limit_accesor limit; + + transaction_guard_t transaction_guard() { + this->begin_transaction(); + auto commitFunc = std::bind(static_cast(&storage_base::commit), this); + auto rollbackFunc = std::bind(static_cast(&storage_base::rollback), this); + return {this->get_connection(), move(commitFunc), move(rollbackFunc)}; + } + + void drop_index(const std::string &indexName) { + auto con = this->get_connection(); + auto db = con.get(); + std::stringstream ss; + ss << "DROP INDEX '" << indexName + "'"; + auto query = ss.str(); + sqlite3_stmt *stmt; + if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { + statement_finalizer finalizer{stmt}; + if(sqlite3_step(stmt) == SQLITE_DONE) { + // done.. + } else { + throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), + sqlite3_errmsg(db)); + } + } else { + throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), + sqlite3_errmsg(db)); + } + } + + void vacuum() { + auto con = this->get_connection(); + auto db = con.get(); + std::string query = "VACUUM"; + sqlite3_stmt *stmt; + if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { + statement_finalizer finalizer{stmt}; + if(sqlite3_step(stmt) == SQLITE_DONE) { + // done.. + } else { + throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), + sqlite3_errmsg(db)); + } + } else { + throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), + sqlite3_errmsg(db)); + } + } + + /** + * Drops table with given name. + */ + void drop_table(const std::string &tableName) { + auto con = this->get_connection(); + this->drop_table_internal(tableName, con.get()); + } + + /** + * Rename table named `from` to `to`. + */ + void rename_table(const std::string &from, const std::string &to) { + auto con = this->get_connection(); + std::stringstream ss; + ss << "ALTER TABLE '" << from << "' RENAME TO '" << to << "'"; + this->perform_query_without_result(ss.str(), con.get()); + } + + /** + * sqlite3_changes function. + */ + int changes() { + auto con = this->get_connection(); + return sqlite3_changes(con.get()); + } + + /** + * sqlite3_total_changes function. + */ + int total_changes() { + auto con = this->get_connection(); + return sqlite3_total_changes(con.get()); + } + + int64 last_insert_rowid() { + auto con = this->get_connection(); + return sqlite3_last_insert_rowid(con.get()); + } + + int busy_timeout(int ms) { + auto con = this->get_connection(); + return sqlite3_busy_timeout(con.get(), ms); + } + + /** + * Returns libsqltie3 lib version, not sqlite_orm + */ + std::string libversion() { + return sqlite3_libversion(); + } + + bool transaction(const std::function &f) { + this->begin_transaction(); + auto con = this->get_connection(); + auto db = con.get(); + auto shouldCommit = f(); + if(shouldCommit) { + this->commit(db); + } else { + this->rollback(db); + } + return shouldCommit; + } + + std::string current_timestamp() { + auto con = this->get_connection(); + return this->current_timestamp(con.get()); + } + +#if SQLITE_VERSION_NUMBER >= 3007010 + /** + * \fn db_release_memory + * \brief Releases freeable memory of database. It is function can/should be called periodically by + * application, if application has less memory usage constraint. \note sqlite3_db_release_memory added + * in 3.7.10 https://sqlite.org/changes.html + */ + int db_release_memory() { + auto con = this->get_connection(); + return sqlite3_db_release_memory(con.get()); + } +#endif + + /** + * Returns existing permanent table names in database. Doesn't check storage itself - works only with + * actual database. + * @return Returns list of tables in database. + */ + std::vector table_names() { + auto con = this->get_connection(); + std::vector tableNames; + std::string sql = "SELECT name FROM sqlite_master WHERE type='table'"; + using data_t = std::vector; + auto db = con.get(); + int res = sqlite3_exec( + db, + sql.c_str(), + [](void *data, int argc, char **argv, char * * /*columnName*/) -> int { + auto &tableNames = *(data_t *)data; + for(int i = 0; i < argc; i++) { + if(argv[i]) { + tableNames.push_back(argv[i]); + } + } + return 0; + }, + &tableNames, + nullptr); + + if(res != SQLITE_OK) { + throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), + sqlite3_errmsg(db)); + } + return tableNames; + } + + void open_forever() { + this->isOpenedForever = true; + this->connection->retain(); + if(1 == this->connection->retain_count()) { + this->on_open_internal(this->connection->get()); + } + } + + void create_collation(const std::string &name, collating_function f) { + collating_function *functionPointer = nullptr; + if(f) { + functionPointer = &(collatingFunctions[name] = std::move(f)); + } else { + collatingFunctions.erase(name); + } + + // create collations if db is open + if(this->connection->retain_count() > 0) { + auto db = this->connection->get(); + if(sqlite3_create_collation(db, + name.c_str(), + SQLITE_UTF8, + functionPointer, + f ? collate_callback : nullptr) != SQLITE_OK) { + throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), + sqlite3_errmsg(db)); + } + } + } + + void begin_transaction() { + this->connection->retain(); + if(1 == this->connection->retain_count()) { + this->on_open_internal(this->connection->get()); + } + auto db = this->connection->get(); + this->begin_transaction(db); + } + + void commit() { + auto db = this->connection->get(); + this->commit(db); + this->connection->release(); + if(this->connection->retain_count() < 0) { + throw std::system_error(std::make_error_code(orm_error_code::no_active_transaction)); + } + } + + void rollback() { + auto db = this->connection->get(); + this->rollback(db); + this->connection->release(); + if(this->connection->retain_count() < 0) { + throw std::system_error(std::make_error_code(orm_error_code::no_active_transaction)); + } + } + + void backup_to(const std::string &filename) { + auto backup = this->make_backup_to(filename); + backup.step(-1); + } + + void backup_to(storage_base &other) { + auto backup = this->make_backup_to(other); + backup.step(-1); + } + + void backup_from(const std::string &filename) { + auto backup = this->make_backup_from(filename); + backup.step(-1); + } + + void backup_from(storage_base &other) { + auto backup = this->make_backup_from(other); + backup.step(-1); + } + + backup_t make_backup_to(const std::string &filename) { + auto holder = std::make_unique(filename); + connection_ref conRef{*holder}; + return {conRef, "main", this->get_connection(), "main", move(holder)}; + } + + backup_t make_backup_to(storage_base &other) { + return {other.get_connection(), "main", this->get_connection(), "main", {}}; + } + + backup_t make_backup_from(const std::string &filename) { + auto holder = std::make_unique(filename); + connection_ref conRef{*holder}; + return {this->get_connection(), "main", conRef, "main", move(holder)}; + } + + backup_t make_backup_from(storage_base &other) { + return {this->get_connection(), "main", other.get_connection(), "main", {}}; + } + + const std::string &filename() const { + return this->connection->filename; + } + + protected: + storage_base(const std::string &filename_, int foreignKeysCount) : + pragma(std::bind(&storage_base::get_connection, this)), + limit(std::bind(&storage_base::get_connection, this)), + inMemory(filename_.empty() || filename_ == ":memory:"), + connection(std::make_unique(filename_)), cachedForeignKeysCount(foreignKeysCount) { + if(this->inMemory) { + this->connection->retain(); + this->on_open_internal(this->connection->get()); + } + } + + storage_base(const storage_base &other) : + on_open(other.on_open), pragma(std::bind(&storage_base::get_connection, this)), + limit(std::bind(&storage_base::get_connection, this)), inMemory(other.inMemory), + connection(std::make_unique(other.connection->filename)), + cachedForeignKeysCount(other.cachedForeignKeysCount) { + if(this->inMemory) { + this->connection->retain(); + this->on_open_internal(this->connection->get()); + } + } + + ~storage_base() { + if(this->isOpenedForever) { + this->connection->release(); + } + if(this->inMemory) { + this->connection->release(); + } + } + + const bool inMemory; + bool isOpenedForever = false; + std::unique_ptr connection; + std::map collatingFunctions; + const int cachedForeignKeysCount; + + connection_ref get_connection() { + connection_ref res{*this->connection}; + if(1 == this->connection->retain_count()) { + this->on_open_internal(this->connection->get()); + } + return res; + } + +#if SQLITE_VERSION_NUMBER >= 3006019 + + void foreign_keys(sqlite3 *db, bool value) { + std::stringstream ss; + ss << "PRAGMA foreign_keys = " << value; + auto query = ss.str(); + auto rc = sqlite3_exec(db, query.c_str(), nullptr, nullptr, nullptr); + if(rc != SQLITE_OK) { + throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), + sqlite3_errmsg(db)); + } + } + + bool foreign_keys(sqlite3 *db) { + std::string query = "PRAGMA foreign_keys"; + auto res = false; + auto rc = sqlite3_exec( + db, + query.c_str(), + [](void *data, int argc, char **argv, char **) -> int { + auto &res = *(bool *)data; + if(argc) { + res = row_extractor().extract(argv[0]); + } + return 0; + }, + &res, + nullptr); + if(rc != SQLITE_OK) { + throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), + sqlite3_errmsg(db)); + } + return res; + } + +#endif + void on_open_internal(sqlite3 *db) { + +#if SQLITE_VERSION_NUMBER >= 3006019 + if(this->cachedForeignKeysCount) { + this->foreign_keys(db, true); + } +#endif + if(this->pragma._synchronous != -1) { + this->pragma.synchronous(this->pragma._synchronous); + } + + if(this->pragma._journal_mode != -1) { + this->pragma.set_pragma("journal_mode", static_cast(this->pragma._journal_mode), db); + } + + for(auto &p: this->collatingFunctions) { + if(sqlite3_create_collation(db, p.first.c_str(), SQLITE_UTF8, &p.second, collate_callback) != + SQLITE_OK) { + throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), + sqlite3_errmsg(db)); + } + } + + for(auto &p: this->limit.limits) { + sqlite3_limit(db, p.first, p.second); + } + + if(this->on_open) { + this->on_open(db); + } + } + + void begin_transaction(sqlite3 *db) { + std::stringstream ss; + ss << "BEGIN TRANSACTION"; + auto query = ss.str(); + sqlite3_stmt *stmt; + if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { + statement_finalizer finalizer{stmt}; + if(sqlite3_step(stmt) == SQLITE_DONE) { + // done.. + } else { + throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), + sqlite3_errmsg(db)); + } + } else { + throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), + sqlite3_errmsg(db)); + } + } + + void commit(sqlite3 *db) { + std::stringstream ss; + ss << "COMMIT"; + auto query = ss.str(); + sqlite3_stmt *stmt; + if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { + statement_finalizer finalizer{stmt}; + if(sqlite3_step(stmt) == SQLITE_DONE) { + // done.. + } else { + throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), + sqlite3_errmsg(db)); + } + } else { + throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), + sqlite3_errmsg(db)); + } + } + + void rollback(sqlite3 *db) { + std::stringstream ss; + ss << "ROLLBACK"; + auto query = ss.str(); + sqlite3_stmt *stmt; + if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { + statement_finalizer finalizer{stmt}; + if(sqlite3_step(stmt) == SQLITE_DONE) { + // done.. + } else { + throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), + sqlite3_errmsg(db)); + } + } else { + throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), + sqlite3_errmsg(db)); + } + } + + std::string current_timestamp(sqlite3 *db) { + std::string res; + std::stringstream ss; + ss << "SELECT CURRENT_TIMESTAMP"; + auto query = ss.str(); + auto rc = sqlite3_exec( + db, + query.c_str(), + [](void *data, int argc, char **argv, char **) -> int { + auto &res = *(std::string *)data; + if(argc) { + if(argv[0]) { + res = row_extractor().extract(argv[0]); + } + } + return 0; + }, + &res, + nullptr); + if(rc != SQLITE_OK) { + throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), + sqlite3_errmsg(db)); + } + return res; + } + + void drop_table_internal(const std::string &tableName, sqlite3 *db) { + std::stringstream ss; + ss << "DROP TABLE '" << tableName + "'"; + this->perform_query_without_result(ss.str(), db); + } + + void perform_query_without_result(const std::string &query, sqlite3 *db) { + sqlite3_stmt *stmt; + if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { + statement_finalizer finalizer{stmt}; + if(sqlite3_step(stmt) == SQLITE_DONE) { + // done.. + } else { + throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), + sqlite3_errmsg(db)); + } + } else { + throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), + sqlite3_errmsg(db)); + } + } + + static int collate_callback(void *arg, int leftLen, const void *lhs, int rightLen, const void *rhs) { + auto &f = *(collating_function *)arg; + return f(leftLen, lhs, rightLen, rhs); + } + + // returns foreign keys count in storage definition + template + static int foreign_keys_count(T &storageImpl) { + auto res = 0; + storageImpl.for_each([&res](auto &impl) { + res += impl.foreign_keys_count(); + }); + return res; + } + }; + } +} + +// #include "prepared_statement.h" + +// #include "expression_object_type.h" + +#include // std::decay +#include // std::reference_wrapper + +// #include "prepared_statement.h" + +namespace sqlite_orm { + + namespace internal { + + template + struct expression_object_type; + + template + struct expression_object_type> { + using type = typename std::decay::type; + }; + + template + struct expression_object_type>> { + using type = typename std::decay::type; + }; + + template + struct expression_object_type> { + using type = typename std::decay::type; + }; + + template + struct expression_object_type>> { + using type = typename std::decay::type; + }; + + template + struct expression_object_type> { + using type = typename std::decay::type; + }; + + template + struct expression_object_type>> { + using type = typename std::decay::type; + }; + + template + struct expression_object_type> { + using type = typename std::decay::type; + }; + + template + struct expression_object_type, Cols...>> { + using type = typename std::decay::type; + }; + + template + struct get_ref_t { + + template + auto &operator()(O &t) const { + return t; + } + }; + + template + struct get_ref_t> { + + template + auto &operator()(O &t) const { + return t.get(); + } + }; + + template + auto &get_ref(T &t) { + using arg_type = typename std::decay::type; + get_ref_t g; + return g(t); + } + + template + struct get_object_t; + + template + struct get_object_t : get_object_t {}; + + template + auto &get_object(T &t) { + using expression_type = typename std::decay::type; + get_object_t obj; + return obj(t); + } + + template + struct get_object_t> { + using expression_type = replace_t; + + template + auto &operator()(O &e) const { + return get_ref(e.obj); + } + }; + + template + struct get_object_t> { + using expression_type = insert_t; + + template + auto &operator()(O &e) const { + return get_ref(e.obj); + } + }; + + template + struct get_object_t> { + using expression_type = update_t; + + template + auto &operator()(O &e) const { + return get_ref(e.obj); + } + }; + } +} + +// #include "statement_serializator.h" + +#include // std::stringstream +#include // std::string +#include // std::enable_if +#include // std::vector +#include // std::iter_swap + +// #include "core_functions.h" + +// #include "constraints.h" + +// #include "conditions.h" + +// #include "column.h" + +// #include "rowid.h" + +// #include "type_printer.h" + +// #include "table_name_collector.h" + +#include // std::set +#include // std::string +#include // std::function +#include // std::type_index + +// #include "select_constraints.h" + +// #include "alias.h" + +// #include "core_functions.h" + +namespace sqlite_orm { + + namespace internal { + + struct table_name_collector { + using table_name_set = std::set>; + using find_table_name_t = std::function; + + find_table_name_t find_table_name; + mutable table_name_set table_names; + + template + table_name_set operator()(const T &) const { + return {}; + } + + template + void operator()(F O::*, std::string alias = {}) const { + if(this->find_table_name) { + table_names.insert(std::make_pair(this->find_table_name(typeid(O)), move(alias))); + } + } + + template + void operator()(const column_pointer &) const { + if(this->find_table_name) { + table_names.insert({this->find_table_name(typeid(T)), ""}); + } + } + + template + void operator()(const alias_column_t &a) const { + (*this)(a.column, alias_extractor::get()); + } + + template + void operator()(const count_asterisk_t &) const { + if(this->find_table_name) { + auto tableName = this->find_table_name(typeid(T)); + if(!tableName.empty()) { + table_names.insert(std::make_pair(move(tableName), "")); + } + } + } + + template + void operator()(const asterisk_t &) const { + if(this->find_table_name) { + auto tableName = this->find_table_name(typeid(T)); + table_names.insert(std::make_pair(move(tableName), "")); + } + } + + template + void operator()(const object_t &) const { + if(this->find_table_name) { + auto tableName = this->find_table_name(typeid(T)); + table_names.insert(std::make_pair(move(tableName), "")); + } + } + }; + + } + +} + +// #include "column_names_getter.h" + +#include // std::string +#include // std::vector +#include // std::reference_wrapper + +// #include "error_code.h" + +// #include "select_constraints.h" + +namespace sqlite_orm { + + namespace internal { + + template + std::string serialize(const T &t, const C &context); + + template + struct column_names_getter { + using expression_type = T; + + template + std::vector operator()(const expression_type &t, const C &context) { + auto newContext = context; + newContext.skip_table_name = false; + auto columnName = serialize(t, newContext); + if(columnName.length()) { + return {move(columnName)}; + } else { + throw std::system_error(std::make_error_code(orm_error_code::column_not_found)); + } + } + }; + + template + std::vector get_column_names(const T &t, const C &context) { + column_names_getter serializator; + return serializator(t, context); + } + + template + struct column_names_getter, void> { + using expression_type = std::reference_wrapper; + + template + std::vector operator()(const expression_type &expression, const C &context) { + return get_column_names(expression.get(), context); + } + }; + + template + struct column_names_getter, void> { + using expression_type = asterisk_t; + + template + std::vector operator()(const expression_type &, const C &) { + std::vector res; + res.push_back("*"); + return res; + } + }; + + template + struct column_names_getter, void> { + using expression_type = object_t; + + template + std::vector operator()(const expression_type &, const C &) { + std::vector res; + res.push_back("*"); + return res; + } + }; + + template + struct column_names_getter, void> { + using expression_type = columns_t; + + template + std::vector operator()(const expression_type &cols, const C &context) { + std::vector columnNames; + columnNames.reserve(static_cast(cols.count)); + auto newContext = context; + newContext.skip_table_name = false; + iterate_tuple(cols.columns, [&columnNames, &newContext](auto &m) { + auto columnName = serialize(m, newContext); + if(columnName.length()) { + columnNames.push_back(columnName); + } else { + throw std::system_error(std::make_error_code(orm_error_code::column_not_found)); + } + }); + return columnNames; + } + }; + + } +} + +// #include "order_by_serializator.h" + +#include // std::string +#include // std::vector +#include // std::stringstream + +namespace sqlite_orm { + + namespace internal { + + template + struct order_by_serializator; + + template + std::string serialize_order_by(const T &t, const C &context) { + order_by_serializator serializator; + return serializator(t, context); + } + + template + struct order_by_serializator, void> { + using statement_type = order_by_t; + + template + std::string operator()(const statement_type &orderBy, const C &context) const { + std::stringstream ss; + auto newContext = context; + newContext.skip_table_name = false; + auto columnName = serialize(orderBy.o, newContext); + ss << columnName << " "; + if(orderBy._collate_argument.length()) { + ss << "COLLATE " << orderBy._collate_argument << " "; + } + switch(orderBy.asc_desc) { + case 1: + ss << "ASC"; + break; + case -1: + ss << "DESC"; + break; + } + return ss.str(); + } + }; + + template + struct order_by_serializator, void> { + using statement_type = dynamic_order_by_t; + + template + std::string operator()(const statement_type &orderBy, const C &) const { + std::vector expressions; + for(auto &entry: orderBy) { + std::string entryString; + { + std::stringstream ss; + ss << entry.name << " "; + if(!entry._collate_argument.empty()) { + ss << "COLLATE " << entry._collate_argument << " "; + } + switch(entry.asc_desc) { + case 1: + ss << "ASC"; + break; + case -1: + ss << "DESC"; + break; + } + entryString = ss.str(); + } + expressions.push_back(move(entryString)); + }; + std::stringstream ss; + ss << static_cast(orderBy) << " "; + for(size_t i = 0; i < expressions.size(); ++i) { + ss << expressions[i]; + if(i < expressions.size() - 1) { + ss << ", "; + } + } + ss << " "; + return ss.str(); + } + }; + + } +} + +// #include "values.h" + +// #include "table_type.h" + +// #include "indexed_column.h" + +namespace sqlite_orm { + + namespace internal { + + template + struct statement_serializator; + + template + std::string serialize(const T &t, const C &context) { + statement_serializator serializator; + return serializator(t, context); + } + + template + struct statement_serializator::value>::type> { + using statement_type = T; + + template + std::string operator()(const statement_type &statement, const C &context) { + if(context.replace_bindable_with_question) { + return "?"; + } else { + return field_printer{}(statement); + } + } + }; + + template + struct statement_serializator, void> { + using statement_type = std::reference_wrapper; + + template + std::string operator()(const statement_type &s, const C &context) { + return serialize(s.get(), context); + } + }; + + template<> + struct statement_serializator { + using statement_type = std::nullptr_t; + + template + std::string operator()(const statement_type &, const C &) { + return "?"; + } + }; + + template + struct statement_serializator, void> { + using statement_type = alias_holder; + + template + std::string operator()(const statement_type &, const C &) { + return T::get(); + } + }; + + template + struct statement_serializator, void> { + using statement_type = core_function_t; + + template + std::string operator()(const statement_type &c, const C &context) const { + std::stringstream ss; + ss << static_cast(c) << "("; + std::vector args; + using args_type = typename std::decay::type::args_type; + args.reserve(std::tuple_size::value); + iterate_tuple(c.args, [&args, &context](auto &v) { + args.push_back(serialize(v, context)); + }); + for(size_t i = 0; i < args.size(); ++i) { + ss << args[i]; + if(i < args.size() - 1) { + ss << ", "; + } + } + ss << ")"; + return ss.str(); + } + }; + + template + struct statement_serializator, void> { + using statement_type = as_t; + + template + std::string operator()(const statement_type &c, const C &context) const { + auto tableAliasString = alias_extractor::get(); + return serialize(c.expression, context) + " AS " + tableAliasString; + } + }; + + template + struct statement_serializator, void> { + using statement_type = alias_column_t; + + template + std::string operator()(const statement_type &c, const C &context) const { + std::stringstream ss; + if(!context.skip_table_name) { + ss << "'" << T::get() << "'."; + } + auto newContext = context; + newContext.skip_table_name = true; + ss << serialize(c.column, newContext); + return ss.str(); + } + }; + + template<> + struct statement_serializator { + using statement_type = std::string; + + template + std::string operator()(const statement_type &c, const C &context) const { + if(context.replace_bindable_with_question) { + return "?"; + } else { + return "\"" + c + "\""; + } + } + }; + + template<> + struct statement_serializator { + using statement_type = const char *; + + template + std::string operator()(const char *c, const C &context) const { + if(context.replace_bindable_with_question) { + return "?"; + } else { + return std::string("'") + c + "'"; + } + } + }; + + template + struct statement_serializator { + using statement_type = F O::*; + + template + std::string operator()(const statement_type &m, const C &context) const { + std::stringstream ss; + if(!context.skip_table_name) { + ss << "\"" << context.impl.find_table_name(typeid(O)) << "\"."; + } + ss << "\"" << context.column_name(m) << "\""; + return ss.str(); + } + }; + + template<> + struct statement_serializator { + using statement_type = rowid_t; + + template + std::string operator()(const statement_type &s, const C &) { + return static_cast(s); + } + }; + + template<> + struct statement_serializator { + using statement_type = oid_t; + + template + std::string operator()(const statement_type &s, const C &) { + return static_cast(s); + } + }; + + template<> + struct statement_serializator<_rowid_t, void> { + using statement_type = _rowid_t; + + template + std::string operator()(const statement_type &s, const C &) { + return static_cast(s); + } + }; + + template + struct statement_serializator, void> { + using statement_type = table_rowid_t; + + template + std::string operator()(const statement_type &s, const C &context) { + std::stringstream ss; + if(!context.skip_table_name) { + ss << "'" << context.impl.find_table_name(typeid(O)) << "'."; + } + ss << static_cast(s); + return ss.str(); + } + }; + + template + struct statement_serializator, void> { + using statement_type = table_oid_t; + + template + std::string operator()(const statement_type &s, const C &context) { + std::stringstream ss; + if(!context.skip_table_name) { + ss << "'" << context.impl.find_table_name(typeid(O)) << "'."; + } + ss << static_cast(s); + return ss.str(); + } + }; + + template + struct statement_serializator, void> { + using statement_type = table__rowid_t; + + template + std::string operator()(const statement_type &s, const C &context) { + std::stringstream ss; + if(!context.skip_table_name) { + ss << "'" << context.impl.find_table_name(typeid(O)) << "'."; + } + ss << static_cast(s); + return ss.str(); + } + }; + + template + struct statement_serializator, void> { + using statement_type = binary_operator; + + template + std::string operator()(const statement_type &c, const C &context) const { + auto lhs = serialize(c.lhs, context); + auto rhs = serialize(c.rhs, context); + std::stringstream ss; + ss << "(" << lhs << " " << static_cast(c) << " " << rhs << ")"; + return ss.str(); + } + }; + + template + struct statement_serializator, void> { + using statement_type = count_asterisk_t; + + template + std::string operator()(const statement_type &, const C &context) const { + return serialize(count_asterisk_without_type{}, context); + } + }; + + template<> + struct statement_serializator { + using statement_type = count_asterisk_without_type; + + template + std::string operator()(const statement_type &c, const C &) const { + std::stringstream ss; + ss << static_cast(c) << "(*)"; + return ss.str(); + } + }; + + template + struct statement_serializator, void> { + using statement_type = distinct_t; + + template + std::string operator()(const statement_type &c, const C &context) const { + std::stringstream ss; + auto expr = serialize(c.t, context); + ss << static_cast(c) << "(" << expr << ")"; + return ss.str(); + } + }; + + template + struct statement_serializator, void> { + using statement_type = all_t; + + template + std::string operator()(const statement_type &c, const C &context) const { + std::stringstream ss; + auto expr = serialize(c.t, context); + ss << static_cast(c) << "(" << expr << ")"; + return ss.str(); + } + }; + + template + struct statement_serializator, void> { + using statement_type = column_pointer; + + template + std::string operator()(const statement_type &c, const C &context) const { + std::stringstream ss; + if(!context.skip_table_name) { + ss << "'" << context.impl.find_table_name(typeid(T)) << "'."; + } + ss << "\"" << context.impl.column_name_simple(c.field) << "\""; + return ss.str(); + } + }; + + template + struct statement_serializator, void> { + using statement_type = cast_t; + + template + std::string operator()(const statement_type &c, const C &context) const { + std::stringstream ss; + ss << static_cast(c) << " ("; + ss << serialize(c.expression, context) << " AS " << type_printer().print() << ")"; + return ss.str(); + } + }; + + template + struct statement_serializator::value>::type> { + using statement_type = T; + + template + std::string operator()(const statement_type &c, const C &context) const { + std::stringstream ss; + ss << serialize(c.left, context) << " "; + ss << static_cast(c) << " "; + ss << serialize(c.right, context); + return ss.str(); + } + }; + + template + struct statement_serializator, void> { + using statement_type = simple_case_t; + + template + std::string operator()(const statement_type &c, const C &context) const { + std::stringstream ss; + ss << "CASE "; + c.case_expression.apply([&ss, context](auto &c) { + ss << serialize(c, context) << " "; + }); + iterate_tuple(c.args, [&ss, context](auto &pair) { + ss << "WHEN " << serialize(pair.first, context) << " "; + ss << "THEN " << serialize(pair.second, context) << " "; + }); + c.else_expression.apply([&ss, context](auto &el) { + ss << "ELSE " << serialize(el, context) << " "; + }); + ss << "END"; + return ss.str(); + } + }; + + template + struct statement_serializator, void> { + using statement_type = is_null_t; + + template + std::string operator()(const statement_type &c, const C &context) const { + std::stringstream ss; + ss << serialize(c.t, context) << " " << static_cast(c); + return ss.str(); + } + }; + + template + struct statement_serializator, void> { + using statement_type = is_not_null_t; + + template + std::string operator()(const statement_type &c, const C &context) const { + std::stringstream ss; + ss << serialize(c.t, context) << " " << static_cast(c); + return ss.str(); + } + }; + + template + struct statement_serializator, void> { + using statement_type = bitwise_not_t; + + template + std::string operator()(const statement_type &c, const C &context) const { + std::stringstream ss; + ss << static_cast(c) << " "; + auto cString = serialize(c.argument, context); + ss << " (" << cString << " )"; + return ss.str(); + } + }; + + template + struct statement_serializator, void> { + using statement_type = negated_condition_t; + + template + std::string operator()(const statement_type &c, const C &context) const { + std::stringstream ss; + ss << static_cast(c) << " "; + auto cString = serialize(c.c, context); + ss << " (" << cString << " )"; + return ss.str(); + } + }; + + template + struct statement_serializator::value>::type> { + using statement_type = T; + + template + std::string operator()(const statement_type &c, const C &context) const { + auto leftString = serialize(c.l, context); + auto rightString = serialize(c.r, context); + std::stringstream ss; + if(context.use_parentheses) { + ss << "("; + } + ss << leftString << " " << static_cast(c) << " " << rightString; + if(context.use_parentheses) { + ss << ")"; + } + return ss.str(); + } + }; + + template + struct statement_serializator, void> { + using statement_type = named_collate; + + template + std::string operator()(const statement_type &c, const C &context) const { + auto newContext = context; + newContext.use_parentheses = false; + auto res = serialize(c.expr, newContext); + return res + " " + static_cast(c); + } + }; + + template + struct statement_serializator, void> { + using statement_type = collate_t; + + template + std::string operator()(const statement_type &c, const C &context) const { + auto newContext = context; + newContext.use_parentheses = false; + auto res = serialize(c.expr, newContext); + return res + " " + static_cast(c); + } + }; + + template + struct statement_serializator, void> { + using statement_type = in_t; + + template + std::string operator()(const statement_type &c, const C &context) const { + std::stringstream ss; + auto leftString = serialize(c.l, context); + ss << leftString << " " << static_cast(c) << " "; + auto newContext = context; + newContext.use_parentheses = true; + ss << serialize(c.arg, newContext); + return ss.str(); + } + }; + + template + struct statement_serializator>, void> { + using statement_type = in_t>; + + template + std::string operator()(const statement_type &c, const C &context) const { + std::stringstream ss; + auto leftString = serialize(c.l, context); + ss << leftString << " " << static_cast(c) << " ( "; + for(size_t index = 0; index < c.arg.size(); ++index) { + auto &value = c.arg[index]; + ss << " " << serialize(value, context); + if(index < c.arg.size() - 1) { + ss << ", "; + } + } + ss << " )"; + return ss.str(); + } + }; + + template + struct statement_serializator, void> { + using statement_type = like_t; + + template + std::string operator()(const statement_type &c, const C &context) const { + std::stringstream ss; + ss << serialize(c.arg, context) << " "; + ss << static_cast(c) << " "; + ss << serialize(c.pattern, context); + c.arg3.apply([&ss, &context](auto &value) { + ss << " ESCAPE " << serialize(value, context); + }); + return ss.str(); + } + }; + + template + struct statement_serializator, void> { + using statement_type = glob_t; + + template + std::string operator()(const statement_type &c, const C &context) const { + std::stringstream ss; + ss << serialize(c.arg, context) << " "; + ss << static_cast(c) << " "; + ss << serialize(c.pattern, context); + return ss.str(); + } + }; + + template + struct statement_serializator, void> { + using statement_type = between_t; + + template + std::string operator()(const statement_type &c, const C &context) const { + std::stringstream ss; + auto expr = serialize(c.expr, context); + ss << expr << " " << static_cast(c) << " "; + ss << serialize(c.b1, context); + ss << " AND "; + ss << serialize(c.b2, context); + return ss.str(); + } + }; + + template + struct statement_serializator, void> { + using statement_type = exists_t; + + template + std::string operator()(const statement_type &c, const C &context) const { + std::stringstream ss; + ss << static_cast(c) << " "; + ss << serialize(c.t, context); + return ss.str(); + } + }; + + template<> + struct statement_serializator { + using statement_type = constraints::autoincrement_t; + + template + std::string operator()(const statement_type &c, const C &) const { + return static_cast(c); + } + }; + + template + struct statement_serializator, void> { + using statement_type = constraints::primary_key_t; + + template + std::string operator()(const statement_type &c, const C &context) const { + auto res = static_cast(c); + using columns_tuple = typename statement_type::columns_tuple; + auto columnsCount = std::tuple_size::value; + if(columnsCount) { + res += "("; + decltype(columnsCount) columnIndex = 0; + iterate_tuple(c.columns, [&context, &res, &columnIndex, columnsCount](auto &column) { + res += context.column_name(column); + if(columnIndex < columnsCount - 1) { + res += ", "; + } + ++columnIndex; + }); + res += ")"; + } + return res; + } + }; + + template + struct statement_serializator, void> { + using statement_type = constraints::unique_t; + + template + std::string operator()(const statement_type &c, const C &context) const { + auto res = static_cast(c); + using columns_tuple = typename statement_type::columns_tuple; + auto columnsCount = std::tuple_size::value; + if(columnsCount) { + res += "("; + decltype(columnsCount) columnIndex = 0; + iterate_tuple(c.columns, [&context, &res, &columnIndex, columnsCount](auto &column) { + res += context.column_name(column); + if(columnIndex < columnsCount - 1) { + res += ", "; + } + ++columnIndex; + }); + res += ")"; + } + return res; + } + }; + + template<> + struct statement_serializator { + using statement_type = constraints::collate_t; + + template + std::string operator()(const statement_type &c, const C &) const { + return static_cast(c); + } + }; + + template + struct statement_serializator, void> { + using statement_type = constraints::default_t; + + template + std::string operator()(const statement_type &c, const C &context) const { + return static_cast(c) + " (" + serialize(c.value, context) + ")"; + } + }; + + template + struct statement_serializator, std::tuple>, void> { + using statement_type = constraints::foreign_key_t, std::tuple>; + + template + std::string operator()(const statement_type &fk, const C &context) const { + std::stringstream ss; + std::vector columnNames; + using columns_type_t = typename std::decay::type::columns_type; + constexpr const size_t columnsCount = std::tuple_size::value; + columnNames.reserve(columnsCount); + iterate_tuple(fk.columns, [&columnNames, &context](auto &v) { + columnNames.push_back(context.impl.column_name(v)); + }); + ss << "FOREIGN KEY("; + for(size_t i = 0; i < columnNames.size(); ++i) { + ss << "'" << columnNames[i] << "'"; + if(i < columnNames.size() - 1) { + ss << ", "; + } + } + ss << ") REFERENCES "; + std::vector referencesNames; + using references_type_t = typename std::decay::type::references_type; + constexpr const size_t referencesCount = std::tuple_size::value; + referencesNames.reserve(referencesCount); + { + using first_reference_t = typename std::tuple_element<0, references_type_t>::type; + using first_reference_mapped_type = typename internal::table_type::type; + auto refTableName = context.impl.find_table_name(typeid(first_reference_mapped_type)); + ss << '\'' << refTableName << '\''; + } + iterate_tuple(fk.references, [&referencesNames, &context](auto &v) { + referencesNames.push_back(context.impl.column_name(v)); + }); + ss << "("; + for(size_t i = 0; i < referencesNames.size(); ++i) { + ss << "'" << referencesNames[i] << "'"; + if(i < referencesNames.size() - 1) { + ss << ", "; + } + } + ss << ")"; + if(fk.on_update) { + ss << ' ' << static_cast(fk.on_update) << " " << fk.on_update._action; + } + if(fk.on_delete) { + ss << ' ' << static_cast(fk.on_delete) << " " << fk.on_delete._action; + } + return ss.str(); + } + }; + + template + struct statement_serializator, void> { + using statement_type = constraints::check_t; + + template + std::string operator()(const statement_type &c, const C &context) const { + return static_cast(c) + " " + serialize(c.expression, context); + } + }; + + template + struct statement_serializator, void> { + using statement_type = column_t; + + template + std::string operator()(const statement_type &c, const C &context) const { + std::stringstream ss; + ss << "'" << c.name << "' "; + using column_type = typename std::decay::type; + using field_type = typename column_type::field_type; + using constraints_type = typename column_type::constraints_type; + ss << type_printer().print() << " "; + { + std::vector constraintsStrings; + constexpr const size_t constraintsCount = std::tuple_size::value; + constraintsStrings.reserve(constraintsCount); + int primaryKeyIndex = -1; + int autoincrementIndex = -1; + int tupleIndex = 0; + iterate_tuple( + c.constraints, + [&constraintsStrings, &primaryKeyIndex, &autoincrementIndex, &tupleIndex, &context](auto &v) { + using constraint_type = typename std::decay::type; + constraintsStrings.push_back(serialize(v, context)); + if(is_primary_key::value) { + primaryKeyIndex = tupleIndex; + } else if(std::is_same::value) { + autoincrementIndex = tupleIndex; + } + ++tupleIndex; + }); + if(primaryKeyIndex != -1 && autoincrementIndex != -1 && autoincrementIndex < primaryKeyIndex) { + iter_swap(constraintsStrings.begin() + primaryKeyIndex, + constraintsStrings.begin() + autoincrementIndex); + } + for(auto &str: constraintsStrings) { + ss << str << ' '; + } + } + if(c.not_null()) { + ss << "NOT NULL "; + } + return ss.str(); + } + }; + + template + struct statement_serializator, void> { + using statement_type = remove_all_t; + + template + std::string operator()(const statement_type &rem, const C &context) const { + auto &tImpl = context.impl.template get_impl(); + std::stringstream ss; + ss << "DELETE FROM '" << tImpl.table.name << "' "; + iterate_tuple(rem.conditions, [&context, &ss](auto &v) { + ss << serialize(v, context); + }); + return ss.str(); + } + }; + + template + struct statement_serializator, void> { + using statement_type = replace_t; + + template + std::string operator()(const statement_type &rep, const C &context) const { + using expression_type = typename std::decay::type; + using object_type = typename expression_object_type::type; + auto &tImpl = context.impl.template get_impl(); + std::stringstream ss; + ss << "REPLACE INTO '" << tImpl.table.name << "' ("; + auto columnNames = tImpl.table.column_names(); + auto columnNamesCount = columnNames.size(); + for(size_t i = 0; i < columnNamesCount; ++i) { + ss << "\"" << columnNames[i] << "\""; + if(i < columnNamesCount - 1) { + ss << ","; + } else { + ss << ")"; + } + ss << " "; + } + ss << "VALUES("; + for(size_t i = 0; i < columnNamesCount; ++i) { + ss << "?"; + if(i < columnNamesCount - 1) { + ss << ", "; + } else { + ss << ")"; + } + } + return ss.str(); + } + }; + + template + struct statement_serializator, void> { + using statement_type = insert_explicit; + + template + std::string operator()(const statement_type &ins, const C &context) const { + constexpr const size_t colsCount = std::tuple_size>::value; + static_assert(colsCount > 0, "Use insert or replace with 1 argument instead"); + using expression_type = typename std::decay::type; + using object_type = typename expression_object_type::type; + auto &tImpl = context.impl.template get_impl(); + std::stringstream ss; + ss << "INSERT INTO '" << tImpl.table.name << "' "; + std::vector columnNames; + columnNames.reserve(colsCount); + { + auto columnsContext = context; + columnsContext.skip_table_name = true; + iterate_tuple(ins.columns.columns, [&columnNames, &columnsContext](auto &m) { + auto columnName = serialize(m, columnsContext); + if(!columnName.empty()) { + columnNames.push_back(columnName); + } else { + throw std::system_error(std::make_error_code(orm_error_code::column_not_found)); + } + }); + } + ss << "("; + for(size_t i = 0; i < columnNames.size(); ++i) { + ss << columnNames[i]; + if(i < columnNames.size() - 1) { + ss << ","; + } else { + ss << ")"; + } + ss << " "; + } + ss << "VALUES ("; + for(size_t i = 0; i < columnNames.size(); ++i) { + ss << "?"; + if(i < columnNames.size() - 1) { + ss << ","; + } else { + ss << ")"; + } + ss << " "; + } + return ss.str(); + } + }; + + template + struct statement_serializator, void> { + using statement_type = update_t; + + template + std::string operator()(const statement_type &upd, const C &context) const { + using expression_type = typename std::decay::type; + using object_type = typename expression_object_type::type; + auto &tImpl = context.impl.template get_impl(); + + std::stringstream ss; + ss << "UPDATE '" << tImpl.table.name << "' SET "; + std::vector setColumnNames; + tImpl.table.for_each_column([&setColumnNames](auto &c) { + if(!c.template has>()) { + setColumnNames.emplace_back(c.name); + } + }); + for(size_t i = 0; i < setColumnNames.size(); ++i) { + ss << "\"" << setColumnNames[i] << "\"" + << " = ?"; + if(i < setColumnNames.size() - 1) { + ss << ","; + } + ss << " "; + } + ss << "WHERE "; + auto primaryKeyColumnNames = tImpl.table.primary_key_column_names(); + for(size_t i = 0; i < primaryKeyColumnNames.size(); ++i) { + ss << "\"" << primaryKeyColumnNames[i] << "\"" + << " = ?"; + if(i < primaryKeyColumnNames.size() - 1) { + ss << " AND"; + } + ss << " "; + } + return ss.str(); + } + }; + + template + struct statement_serializator, Wargs...>, void> { + using statement_type = update_all_t, Wargs...>; + + template + std::string operator()(const statement_type &upd, const C &context) const { + std::stringstream ss; + ss << "UPDATE "; + table_name_collector collector{[&context](std::type_index ti) { + return context.impl.find_table_name(ti); + }}; + iterate_ast(upd.set.assigns, collector); + if(!collector.table_names.empty()) { + if(collector.table_names.size() == 1) { + ss << " '" << collector.table_names.begin()->first << "' "; + ss << static_cast(upd.set) << " "; + std::vector setPairs; + auto leftContext = context; + leftContext.skip_table_name = true; + iterate_tuple(upd.set.assigns, [&context, &leftContext, &setPairs](auto &asgn) { + std::stringstream sss; + sss << serialize(asgn.lhs, leftContext); + sss << " " << static_cast(asgn) << " "; + sss << serialize(asgn.rhs, context) << " "; + setPairs.push_back(sss.str()); + }); + auto setPairsCount = setPairs.size(); + for(size_t i = 0; i < setPairsCount; ++i) { + ss << setPairs[i] << " "; + if(i < setPairsCount - 1) { + ss << ", "; + } + } + iterate_tuple(upd.conditions, [&context, &ss](auto &v) { + ss << serialize(v, context); + }); + return ss.str(); + } else { + throw std::system_error(std::make_error_code(orm_error_code::too_many_tables_specified)); + } + } else { + throw std::system_error(std::make_error_code(orm_error_code::incorrect_set_fields_specified)); + } + } + }; + + template + struct statement_serializator, void> { + using statement_type = insert_t; + + template + std::string operator()(const statement_type &, const C &context) const { + using object_type = typename expression_object_type::type; + auto &tImpl = context.impl.template get_impl(); + std::stringstream ss; + ss << "INSERT INTO '" << tImpl.table.name << "' "; + std::vector columnNames; + auto compositeKeyColumnNames = tImpl.table.composite_key_columns_names(); + + tImpl.table.for_each_column([&tImpl, &columnNames, &compositeKeyColumnNames](auto &c) { + if(tImpl.table._without_rowid || !c.template has>()) { + auto it = find(compositeKeyColumnNames.begin(), compositeKeyColumnNames.end(), c.name); + if(it == compositeKeyColumnNames.end()) { + columnNames.emplace_back(c.name); + } + } + }); + + auto columnNamesCount = columnNames.size(); + if(columnNamesCount) { + ss << "("; + for(size_t i = 0; i < columnNamesCount; ++i) { + ss << "\"" << columnNames[i] << "\""; + if(i < columnNamesCount - 1) { + ss << ","; + } else { + ss << ")"; + } + ss << " "; + } + } else { + ss << "DEFAULT "; + } + ss << "VALUES "; + if(columnNamesCount) { + ss << "("; + for(size_t i = 0; i < columnNamesCount; ++i) { + ss << "?"; + if(i < columnNamesCount - 1) { + ss << ", "; + } else { + ss << ")"; + } + } + } + return ss.str(); + } + }; + + template + struct statement_serializator, void> { + using statement_type = remove_t; + + template + std::string operator()(const statement_type &, const C &context) const { + auto &tImpl = context.impl.template get_impl(); + std::stringstream ss; + ss << "DELETE FROM '" << tImpl.table.name << "' "; + ss << "WHERE "; + auto primaryKeyColumnNames = tImpl.table.primary_key_column_names(); + for(size_t i = 0; i < primaryKeyColumnNames.size(); ++i) { + ss << "\"" << primaryKeyColumnNames[i] << "\"" + << " = ? "; + if(i < primaryKeyColumnNames.size() - 1) { + ss << "AND "; + } + } + return ss.str(); + } + }; + + template + struct statement_serializator, void> { + using statement_type = replace_range_t; + + template + std::string operator()(const statement_type &rep, const C &context) const { + using expression_type = typename std::decay::type; + using object_type = typename expression_type::object_type; + auto &tImpl = context.impl.template get_impl(); + std::stringstream ss; + ss << "REPLACE INTO '" << tImpl.table.name << "' ("; + auto columnNames = tImpl.table.column_names(); + auto columnNamesCount = columnNames.size(); + for(size_t i = 0; i < columnNamesCount; ++i) { + ss << "\"" << columnNames[i] << "\""; + if(i < columnNamesCount - 1) { + ss << ", "; + } else { + ss << ") "; + } + } + ss << "VALUES "; + auto valuesString = [columnNamesCount] { + std::stringstream ss; + ss << "("; + for(size_t i = 0; i < columnNamesCount; ++i) { + ss << "?"; + if(i < columnNamesCount - 1) { + ss << ", "; + } else { + ss << ")"; + } + } + return ss.str(); + }(); + auto valuesCount = static_cast(std::distance(rep.range.first, rep.range.second)); + for(auto i = 0; i < valuesCount; ++i) { + ss << valuesString; + if(i < valuesCount - 1) { + ss << ","; + } + ss << " "; + } + return ss.str(); + } + }; + + template + struct statement_serializator, void> { + using statement_type = insert_range_t; + + template + std::string operator()(const statement_type &statement, const C &context) const { + using expression_type = typename std::decay::type; + using object_type = typename expression_type::object_type; + auto &tImpl = context.impl.template get_impl(); + + std::stringstream ss; + ss << "INSERT INTO '" << tImpl.table.name << "' ("; + std::vector columnNames; + tImpl.table.for_each_column([&columnNames](auto &c) { + if(!c.template has>()) { + columnNames.emplace_back(c.name); + } + }); + + auto columnNamesCount = columnNames.size(); + for(size_t i = 0; i < columnNamesCount; ++i) { + ss << "\"" << columnNames[i] << "\""; + if(i < columnNamesCount - 1) { + ss << ","; + } else { + ss << ")"; + } + ss << " "; + } + ss << "VALUES "; + auto valuesString = [columnNamesCount] { + std::stringstream ss; + ss << "("; + for(size_t i = 0; i < columnNamesCount; ++i) { + ss << "?"; + if(i < columnNamesCount - 1) { + ss << ", "; + } else { + ss << ")"; + } + } + return ss.str(); + }(); + auto valuesCount = static_cast(std::distance(statement.range.first, statement.range.second)); + for(auto i = 0; i < valuesCount; ++i) { + ss << valuesString; + if(i < valuesCount - 1) { + ss << ","; + } + ss << " "; + } + return ss.str(); + } + }; + + template + std::string serialize_get_all_impl(const T &get, const C &context) { + using primary_type = typename T::type; + + table_name_collector collector; + collector.table_names.insert( + std::make_pair(context.impl.find_table_name(typeid(primary_type)), std::string{})); + iterate_ast(get.conditions, collector); + std::stringstream ss; + ss << "SELECT "; + auto &tImpl = context.impl.template get_impl(); + auto columnNames = tImpl.table.column_names(); + for(size_t i = 0; i < columnNames.size(); ++i) { + ss << "\"" << tImpl.table.name << "\"." + << "\"" << columnNames[i] << "\""; + if(i < columnNames.size() - 1) { + ss << ", "; + } else { + ss << " "; + } + } + ss << "FROM "; + std::vector> tableNames(collector.table_names.begin(), + collector.table_names.end()); + for(size_t i = 0; i < tableNames.size(); ++i) { + auto &tableNamePair = tableNames[i]; + ss << "'" << tableNamePair.first << "' "; + if(!tableNamePair.second.empty()) { + ss << tableNamePair.second << " "; + } + if(int(i) < int(tableNames.size()) - 1) { + ss << ","; + } + ss << " "; + } + iterate_tuple(get.conditions, [&context, &ss](auto &v) { + ss << serialize(v, context); + }); + return ss.str(); + } + +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + template + struct statement_serializator, void> { + using statement_type = get_all_optional_t; + + template + std::string operator()(const statement_type &get, const C &context) const { + return serialize_get_all_impl(get, context); + } + }; +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED + + template + struct statement_serializator, void> { + using statement_type = get_all_pointer_t; + + template + std::string operator()(const statement_type &get, const C &context) const { + return serialize_get_all_impl(get, context); + } + }; + + template + struct statement_serializator, void> { + using statement_type = get_all_t; + + template + std::string operator()(const statement_type &get, const C &context) const { + return serialize_get_all_impl(get, context); + } + }; + + template + std::string serialize_get_impl(const T &, const C &context) { + using primary_type = typename T::type; + auto &tImpl = context.impl.template get_impl(); + std::stringstream ss; + ss << "SELECT "; + auto columnNames = tImpl.table.column_names(); + for(size_t i = 0; i < columnNames.size(); ++i) { + ss << "\"" << columnNames[i] << "\""; + if(i < columnNames.size() - 1) { + ss << ","; + } + ss << " "; + } + ss << "FROM '" << tImpl.table.name << "' WHERE "; + auto primaryKeyColumnNames = tImpl.table.primary_key_column_names(); + if(!primaryKeyColumnNames.empty()) { + for(size_t i = 0; i < primaryKeyColumnNames.size(); ++i) { + ss << "\"" << primaryKeyColumnNames[i] << "\"" + << " = ? "; + if(i < primaryKeyColumnNames.size() - 1) { + ss << "AND"; + } + ss << ' '; + } + return ss.str(); + } else { + throw std::system_error(std::make_error_code(orm_error_code::table_has_no_primary_key_column)); + } + } + + template + struct statement_serializator, void> { + using statement_type = get_t; + + template + std::string operator()(const statement_type &get, const C &context) const { + return serialize_get_impl(get, context); + } + }; + + template + struct statement_serializator, void> { + using statement_type = get_pointer_t; + + template + std::string operator()(const statement_type &get, const C &context) const { + return serialize_get_impl(get, context); + } + }; + +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + template + struct statement_serializator, void> { + using statement_type = get_optional_t; + + template + std::string operator()(const statement_type &get, const C &context) const { + return serialize_get_impl(get, context); + } + }; +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED + template + struct statement_serializator, void> { + using statement_type = select_t; + + template + std::string operator()(const statement_type &sel, const C &context) const { + std::stringstream ss; + if(!is_base_of_template::value) { + if(!sel.highest_level) { + ss << "( "; + } + ss << "SELECT "; + } + if(get_distinct(sel.col)) { + ss << static_cast(distinct(0)) << " "; + } + auto columnNames = get_column_names(sel.col, context); + for(size_t i = 0; i < columnNames.size(); ++i) { + ss << columnNames[i]; + if(i < columnNames.size() - 1) { + ss << ","; + } + ss << " "; + } + table_name_collector collector{[&context](std::type_index ti) { + return context.impl.find_table_name(ti); + }}; + iterate_ast(sel.col, collector); + iterate_ast(sel.conditions, collector); + internal::join_iterator()([&collector, &context](const auto &c) { + using original_join_type = typename std::decay::type::join_type::type; + using cross_join_type = typename internal::mapped_type_proxy::type; + auto crossJoinedTableName = context.impl.find_table_name(typeid(cross_join_type)); + auto tableAliasString = alias_extractor::get(); + std::pair tableNameWithAlias(std::move(crossJoinedTableName), + std::move(tableAliasString)); + collector.table_names.erase(tableNameWithAlias); + }); + if(!collector.table_names.empty()) { + ss << "FROM "; + std::vector> tableNames(collector.table_names.begin(), + collector.table_names.end()); + for(size_t i = 0; i < tableNames.size(); ++i) { + auto &tableNamePair = tableNames[i]; + ss << "'" << tableNamePair.first << "' "; + if(!tableNamePair.second.empty()) { + ss << tableNamePair.second << " "; + } + if(int(i) < int(tableNames.size()) - 1) { + ss << ","; + } + ss << " "; + } + } + iterate_tuple(sel.conditions, [&context, &ss](auto &v) { + ss << serialize(v, context); + }); + if(!is_base_of_template::value) { + if(!sel.highest_level) { + ss << ") "; + } + } + return ss.str(); + } + }; + + template + struct statement_serializator, void> { + using statement_type = indexed_column_t; + + template + std::string operator()(const statement_type &statement, const C &context) const { + std::stringstream ss; + ss << serialize(statement.column_or_expression, context); + if(!statement._collation_name.empty()) { + ss << " COLLATE " << statement._collation_name; + } + if(statement._order) { + switch(statement._order) { + case -1: + ss << " DESC"; + break; + case 1: + ss << " ASC"; + break; + default: + throw std::system_error(std::make_error_code(orm_error_code::incorrect_order)); + } + } + return ss.str(); + } + }; + + template + struct statement_serializator, void> { + using statement_type = index_t; + + template + std::string operator()(const statement_type &statement, const C &context) const { + std::stringstream ss; + ss << "CREATE "; + if(statement.unique) { + ss << "UNIQUE "; + } + using columns_type = typename std::decay::type::columns_type; + using head_t = typename std::tuple_element<0, columns_type>::type::column_type; + using indexed_type = typename table_type::type; + ss << "INDEX IF NOT EXISTS '" << statement.name << "' ON '" + << context.impl.find_table_name(typeid(indexed_type)) << "' ("; + std::vector columnNames; + iterate_tuple(statement.columns, [&columnNames, &context](auto &v) { + columnNames.push_back(context.column_name(v.column_or_expression)); + }); + for(size_t i = 0; i < columnNames.size(); ++i) { + ss << "'" << columnNames[i] << "'"; + if(i < columnNames.size() - 1) { + ss << ", "; + } + } + ss << ")"; + return ss.str(); + } + }; + + template + struct statement_serializator, void> { + using statement_type = where_t; + + template + std::string operator()(const statement_type &w, const C &context) const { + std::stringstream ss; + ss << static_cast(w) << " "; + auto whereString = serialize(w.c, context); + ss << "( " << whereString << ") "; + return ss.str(); + } + }; + + template + struct statement_serializator, void> { + using statement_type = order_by_t; + + template + std::string operator()(const statement_type &orderBy, const C &context) const { + std::stringstream ss; + ss << static_cast(orderBy) << " "; + auto orderByString = serialize_order_by(orderBy, context); + ss << orderByString << " "; + return ss.str(); + } + }; + + template + struct statement_serializator, void> { + using statement_type = dynamic_order_by_t; + + template + std::string operator()(const statement_type &orderBy, const CC &context) const { + return serialize_order_by(orderBy, context); + } + }; + + template + struct statement_serializator, void> { + using statement_type = multi_order_by_t; + + template + std::string operator()(const statement_type &orderBy, const C &context) const { + std::stringstream ss; + std::vector expressions; + iterate_tuple(orderBy.args, [&expressions, &context](auto &v) { + auto expression = serialize_order_by(v, context); + expressions.push_back(move(expression)); + }); + ss << static_cast(orderBy) << " "; + for(size_t i = 0; i < expressions.size(); ++i) { + ss << expressions[i]; + if(i < expressions.size() - 1) { + ss << ", "; + } + } + ss << " "; + return ss.str(); + } + }; + + template + struct statement_serializator, void> { + using statement_type = cross_join_t; + + template + std::string operator()(const statement_type &c, const C &context) const { + std::stringstream ss; + ss << static_cast(c) << " "; + ss << " '" << context.impl.find_table_name(typeid(O)) << "'"; + return ss.str(); + } + }; + + template + struct statement_serializator, void> { + using statement_type = inner_join_t; + + template + std::string operator()(const statement_type &l, const C &context) const { + std::stringstream ss; + ss << static_cast(l) << " "; + auto aliasString = alias_extractor::get(); + ss << " '" << context.impl.find_table_name(typeid(typename mapped_type_proxy::type)) << "' "; + if(aliasString.length()) { + ss << "'" << aliasString << "' "; + } + ss << serialize(l.constraint, context); + return ss.str(); + } + }; + + template + struct statement_serializator, void> { + using statement_type = on_t; + + template + std::string operator()(const statement_type &t, const C &context) const { + std::stringstream ss; + auto newContext = context; + newContext.skip_table_name = false; + ss << static_cast(t) << " " << serialize(t.arg, newContext) << " "; + return ss.str(); + } + }; + + template + struct statement_serializator, void> { + using statement_type = join_t; + + template + std::string operator()(const statement_type &l, const C &context) const { + std::stringstream ss; + ss << static_cast(l) << " "; + ss << " '" << context.impl.find_table_name(typeid(T)) << "' "; + ss << serialize(l.constraint, context); + return ss.str(); + } + }; + + template + struct statement_serializator, void> { + using statement_type = left_join_t; + + template + std::string operator()(const statement_type &l, const C &context) const { + std::stringstream ss; + ss << static_cast(l) << " "; + ss << " '" << context.impl.find_table_name(typeid(T)) << "' "; + ss << serialize(l.constraint, context); + return ss.str(); + } + }; + + template + struct statement_serializator, void> { + using statement_type = left_outer_join_t; + + template + std::string operator()(const statement_type &l, const C &context) const { + std::stringstream ss; + ss << static_cast(l) << " "; + ss << " '" << context.impl.find_table_name(typeid(T)) << "' "; + ss << serialize(l.constraint, context); + return ss.str(); + } + }; + + template + struct statement_serializator, void> { + using statement_type = natural_join_t; + + template + std::string operator()(const statement_type &c, const C &context) const { + std::stringstream ss; + ss << static_cast(c) << " "; + ss << " '" << context.impl.find_table_name(typeid(O)) << "'"; + return ss.str(); + } + }; + + template + struct statement_serializator, void> { + using statement_type = group_by_t; + + template + std::string operator()(const statement_type &groupBy, const C &context) const { + std::stringstream ss; + std::vector expressions; + auto newContext = context; + newContext.skip_table_name = false; + iterate_tuple(groupBy.args, [&expressions, &newContext](auto &v) { + auto expression = serialize(v, newContext); + expressions.push_back(expression); + }); + ss << static_cast(groupBy) << " "; + for(size_t i = 0; i < expressions.size(); ++i) { + ss << expressions[i]; + if(i < expressions.size() - 1) { + ss << ", "; + } + } + ss << " "; + return ss.str(); + } + }; + + template + struct statement_serializator, void> { + using statement_type = having_t; + + template + std::string operator()(const statement_type &hav, const C &context) const { + std::stringstream ss; + auto newContext = context; + newContext.skip_table_name = false; + ss << static_cast(hav) << " "; + ss << serialize(hav.t, newContext) << " "; + return ss.str(); + } + }; + + /** + * HO - has offset + * OI - offset is implicit + */ + template + struct statement_serializator, void> { + using statement_type = limit_t; + + template + std::string operator()(const statement_type &limt, const C &context) const { + auto newContext = context; + newContext.skip_table_name = false; + std::stringstream ss; + ss << static_cast(limt) << " "; + if(HO) { + if(OI) { + limt.off.apply([&newContext, &ss](auto &value) { + ss << serialize(value, newContext); + }); + ss << ", "; + ss << serialize(limt.lim, newContext); + } else { + ss << serialize(limt.lim, newContext) << " OFFSET "; + limt.off.apply([&newContext, &ss](auto &value) { + ss << serialize(value, newContext); + }); + } + } else { + ss << serialize(limt.lim, newContext); + } + return ss.str(); + } + }; + + template + struct statement_serializator, void> { + using statement_type = using_t; + + template + std::string operator()(const statement_type &statement, const C &context) const { + auto newContext = context; + newContext.skip_table_name = true; + return static_cast(statement) + " (" + serialize(statement.column, newContext) + " )"; + } + }; + + template + struct statement_serializator, void> { + using statement_type = std::tuple; + + template + std::string operator()(const statement_type &statement, const C &context) const { + std::stringstream ss; + ss << '('; + auto index = 0; + using TupleSize = std::tuple_size; + iterate_tuple(statement, [&context, &index, &ss](auto &value) { + ss << serialize(value, context); + if(index < TupleSize::value - 1) { + ss << ", "; + } + ++index; + }); + ss << ')'; + return ss.str(); + } + }; + + template + struct statement_serializator, void> { + using statement_type = values_t; + + template + std::string operator()(const statement_type &statement, const C &context) const { + std::stringstream ss; + if(context.use_parentheses) { + ss << '('; + } + ss << "VALUES "; + { + auto index = 0; + auto &tuple = statement.tuple; + using tuple_type = typename std::decay::type; + using TupleSize = std::tuple_size; + iterate_tuple(tuple, [&context, &index, &ss](auto &value) { + ss << serialize(value, context); + if(index < TupleSize::value - 1) { + ss << ", "; + } + ++index; + }); + } + if(context.use_parentheses) { + ss << ')'; + } + return ss.str(); + } + }; + + template + struct statement_serializator, void> { + using statement_type = dynamic_values_t; + + template + std::string operator()(const statement_type &statement, const C &context) const { + std::stringstream ss; + if(context.use_parentheses) { + ss << '('; + } + ss << "VALUES "; + { + auto vectorSize = statement.vector.size(); + for(decltype(vectorSize) index = 0; index < vectorSize; ++index) { + auto &value = statement.vector[index]; + ss << serialize(value, context); + if(index < vectorSize - 1) { + ss << ", "; + } + } + } + if(context.use_parentheses) { + ss << ')'; + } + return ss.str(); + } + }; + + } +} + +// #include "table_name_collector.h" + +// #include "object_from_column_builder.h" + +namespace sqlite_orm { + + namespace internal { + + /** + * Storage class itself. Create an instanse to use it as an interfacto to sqlite db by calling `make_storage` + * function. + */ + template + struct storage_t : storage_base { + using self = storage_t; + using impl_type = storage_impl; + + /** + * @param filename database filename. + * @param impl_ storage_impl head + */ + storage_t(const std::string &filename, impl_type impl_) : + storage_base{filename, foreign_keys_count(impl_)}, impl(std::move(impl_)) {} + + storage_t(const storage_t &other) : storage_base(other), impl(other.impl) {} + + protected: + impl_type impl; + + template + friend struct view_t; + + template + friend struct dynamic_order_by_t; + + template + friend struct iterator_t; + + template + friend struct serializator_context_builder; + + template + void create_table(sqlite3 *db, const std::string &tableName, const I &tableImpl) { + std::stringstream ss; + ss << "CREATE TABLE '" << tableName << "' ( "; + auto columnsCount = tableImpl.table.columns_count; + auto index = 0; + using context_t = serializator_context; + context_t context{this->impl}; + iterate_tuple(tableImpl.table.columns, [columnsCount, &index, &ss, &context](auto &c) { + ss << serialize(c, context); + if(index < columnsCount - 1) { + ss << ", "; + } + index++; + }); + ss << ") "; + if(tableImpl.table._without_rowid) { + ss << "WITHOUT ROWID "; + } + auto query = ss.str(); + sqlite3_stmt *stmt; + if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { + statement_finalizer finalizer{stmt}; + if(sqlite3_step(stmt) == SQLITE_DONE) { + // done.. + } else { + throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), + sqlite3_errmsg(db)); + } + } else { + throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), + sqlite3_errmsg(db)); + } + } + + template + void backup_table(sqlite3 *db, const I &tableImpl, const std::vector &columnsToIgnore) { + + // here we copy source table to another with a name with '_backup' suffix, but in case table with such + // a name already exists we append suffix 1, then 2, etc until we find a free name.. + auto backupTableName = tableImpl.table.name + "_backup"; + if(tableImpl.table_exists(backupTableName, db)) { + int suffix = 1; + do { + std::stringstream stream; + stream << suffix; + auto anotherBackupTableName = backupTableName + stream.str(); + if(!tableImpl.table_exists(anotherBackupTableName, db)) { + backupTableName = anotherBackupTableName; + break; + } + ++suffix; + } while(true); + } + + this->create_table(db, backupTableName, tableImpl); + + tableImpl.copy_table(db, backupTableName, columnsToIgnore); + + this->drop_table_internal(tableImpl.table.name, db); + + tableImpl.rename_table(db, backupTableName, tableImpl.table.name); + } + + template + void assert_mapped_type() const { + using mapped_types_tuples = std::tuple; + static_assert(tuple_helper::has_type::value, "type is not mapped to a storage"); + } + + template + auto &get_impl() const { + return this->impl.template get_impl(); + } + + template + auto &get_impl() { + return this->impl.template get_impl(); + } + + public: + template + view_t iterate(Args &&... args) { + this->assert_mapped_type(); + + auto con = this->get_connection(); + return {*this, std::move(con), std::forward(args)...}; + } + + /** + * Delete from routine. + * O is an object's type. Must be specified explicitly. + * @param args optional conditions: `where`, `join` etc + * @example: storage.remove_all(); - DELETE FROM users + * @example: storage.remove_all(where(in(&User::id, {5, 6, 7}))); - DELETE FROM users WHERE id IN (5, 6, 7) + */ + template + void remove_all(Args &&... args) { + this->assert_mapped_type(); + auto statement = this->prepare(sqlite_orm::remove_all(std::forward(args)...)); + this->execute(statement); + } + + /** + * Delete routine. + * O is an object's type. Must be specified explicitly. + * @param ids ids of object to be removed. + */ + template + void remove(Ids... ids) { + this->assert_mapped_type(); + auto statement = this->prepare(sqlite_orm::remove(std::forward(ids)...)); + this->execute(statement); + } + + /** + * Update routine. Sets all non primary key fields where primary key is equal. + * O is an object type. May be not specified explicitly cause it can be deduced by + * compiler from first parameter. + * @param o object to be updated. + */ + template + void update(const O &o) { + this->assert_mapped_type(); + auto statement = this->prepare(sqlite_orm::update(std::ref(o))); + this->execute(statement); + } + + template + void update_all(internal::set_t set, Wargs... wh) { + auto statement = this->prepare(sqlite_orm::update_all(std::move(set), std::forward(wh)...)); + this->execute(statement); + } + + protected: + template + std::string group_concat_internal(F O::*m, std::unique_ptr y, Args &&... args) { + this->assert_mapped_type(); + std::vector rows; + if(y) { + rows = this->select(sqlite_orm::group_concat(m, move(*y)), std::forward(args)...); + } else { + rows = this->select(sqlite_orm::group_concat(m), std::forward(args)...); + } + if(!rows.empty()) { + return move(rows.front()); + } else { + return {}; + } + } + + public: + /** + * SELECT * routine. + * O is an object type to be extracted. Must be specified explicitly. + * @return All objects of type O stored in database at the moment in `std::vector`. + * @note If you need to return the result in a different container type then use a different `get_all` function overload `get_all>` + * @example: storage.get_all() - SELECT * FROM users + * @example: storage.get_all(where(like(&User::name, "N%")), order_by(&User::id)); - SELECT * FROM users WHERE name LIKE 'N%' ORDER BY id + */ + template + auto get_all(Args &&... args) { + this->assert_mapped_type(); + auto statement = this->prepare(sqlite_orm::get_all(std::forward(args)...)); + return this->execute(statement); + } + + /** + * SELECT * routine. + * O is an object type to be extracted. Must be specified explicitly. + * R is an explicit return type. This type must have `push_back(O &&)` function. + * @return All objects of type O stored in database at the moment in `R`. + * @example: storage.get_all>(); - SELECT * FROM users + * @example: storage.get_all>(where(like(&User::name, "N%")), order_by(&User::id)); - SELECT * FROM users WHERE name LIKE 'N%' ORDER BY id + */ + template + auto get_all(Args &&... args) { + this->assert_mapped_type(); + auto statement = this->prepare(sqlite_orm::get_all(std::forward(args)...)); + return this->execute(statement); + } + + /** + * SELECT * routine. + * O is an object type to be extracted. Must be specified explicitly. + * @return All objects of type O as `std::unique_ptr` inside a `std::vector` stored in database at the moment. + * @note If you need to return the result in a different container type then use a different `get_all_pointer` function overload `get_all_pointer>` + * @example: storage.get_all_pointer(); - SELECT * FROM users + * @example: storage.get_all_pointer(where(length(&User::name) > 6)); - SELECT * FROM users WHERE LENGTH(name) > 6 + */ + template + auto get_all_pointer(Args &&... args) { + this->assert_mapped_type(); + auto statement = this->prepare(sqlite_orm::get_all_pointer(std::forward(args)...)); + return this->execute(statement); + } + + /** + * SELECT * routine. + * O is an object type to be extracted. Must be specified explicitly. + * R is a container type. std::vector> is default + * @return All objects of type O as std::unique_ptr stored in database at the moment. + * @example: storage.get_all_pointer>(); - SELECT * FROM users + * @example: storage.get_all_pointer>(where(length(&User::name) > 6)); - SELECT * FROM users WHERE LENGTH(name) > 6 + */ + template + auto get_all_pointer(Args &&... args) { + this->assert_mapped_type(); + auto statement = this->prepare(sqlite_orm::get_all_pointer(std::forward(args)...)); + return this->execute(statement); + } + + /** + * Select * by id routine. + * throws std::system_error(orm_error_code::not_found, orm_error_category) if object not found with given + * id. throws std::system_error with orm_error_category in case of db error. O is an object type to be + * extracted. Must be specified explicitly. + * @return Object of type O where id is equal parameter passed or throws + * `std::system_error(orm_error_code::not_found, orm_error_category)` if there is no object with such id. + */ + template + O get(Ids... ids) { + this->assert_mapped_type(); + auto statement = this->prepare(sqlite_orm::get(std::forward(ids)...)); + return this->execute(statement); + } + + /** + * The same as `get` function but doesn't throw an exception if noting found but returns std::unique_ptr + * with null value. throws std::system_error in case of db error. + */ + template + std::unique_ptr get_pointer(Ids... ids) { + this->assert_mapped_type(); + auto statement = this->prepare(sqlite_orm::get_pointer(std::forward(ids)...)); + return this->execute(statement); + } + + /** + * A previous version of get_pointer() that returns a shared_ptr + * instead of a unique_ptr. New code should prefer get_pointer() + * unless the data needs to be shared. + * + * @note + * Most scenarios don't need shared ownership of data, so we should prefer + * unique_ptr when possible. It's more efficient, doesn't require atomic + * ops for a reference count (which can cause major slowdowns on + * weakly-ordered platforms like ARM), and can be easily promoted to a + * shared_ptr, exactly like we're doing here. + * (Conversely, you _can't_ go from shared back to unique.) + */ + template + std::shared_ptr get_no_throw(Ids... ids) { + return std::shared_ptr(get_pointer(std::forward(ids)...)); + } + +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + /** + * The same as `get` function but doesn't throw an exception if noting found but + * returns an empty std::optional. throws std::system_error in case of db error. + */ + template + std::optional get_optional(Ids... ids) { + this->assert_mapped_type(); + auto statement = this->prepare(sqlite_orm::get_optional(std::forward(ids)...)); + return this->execute(statement); + } +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED + + /** + * SELECT COUNT(*) https://www.sqlite.org/lang_aggfunc.html#count + * @return Number of O object in table. + */ + template::type> + int count(Args &&... args) { + this->assert_mapped_type(); + auto rows = this->select(sqlite_orm::count(), std::forward(args)...); + if(!rows.empty()) { + return rows.front(); + } else { + return 0; + } + } + + /** + * SELECT COUNT(X) https://www.sqlite.org/lang_aggfunc.html#count + * @param m member pointer to class mapped to the storage. + */ + template + int count(F O::*m, Args &&... args) { + this->assert_mapped_type(); + auto rows = this->select(sqlite_orm::count(m), std::forward(args)...); + if(!rows.empty()) { + return rows.front(); + } else { + return 0; + } + } + + /** + * AVG(X) query. https://www.sqlite.org/lang_aggfunc.html#avg + * @param m is a class member pointer (the same you passed into make_column). + * @return average value from db. + */ + template + double avg(F O::*m, Args &&... args) { + this->assert_mapped_type(); + auto rows = this->select(sqlite_orm::avg(m), std::forward(args)...); + if(!rows.empty()) { + return rows.front(); + } else { + return 0; + } + } + + template + std::string group_concat(F O::*m) { + return this->group_concat_internal(m, {}); + } + + /** + * GROUP_CONCAT(X) query. https://www.sqlite.org/lang_aggfunc.html#groupconcat + * @param m is a class member pointer (the same you passed into make_column). + * @return group_concat query result. + */ + template, + typename sfinae = typename std::enable_if::value >= 1>::type> + std::string group_concat(F O::*m, Args &&... args) { + return this->group_concat_internal(m, {}, std::forward(args)...); + } + + /** + * GROUP_CONCAT(X, Y) query. https://www.sqlite.org/lang_aggfunc.html#groupconcat + * @param m is a class member pointer (the same you passed into make_column). + * @return group_concat query result. + */ + template + std::string group_concat(F O::*m, std::string y, Args &&... args) { + return this->group_concat_internal(m, + std::make_unique(move(y)), + std::forward(args)...); + } + + template + std::string group_concat(F O::*m, const char *y, Args &&... args) { + std::unique_ptr str; + if(y) { + str = std::make_unique(y); + } else { + str = std::make_unique(); + } + return this->group_concat_internal(m, move(str), std::forward(args)...); + } + + /** + * MAX(x) query. + * @param m is a class member pointer (the same you passed into make_column). + * @return std::unique_ptr with max value or null if sqlite engine returned null. + */ + template::type> + std::unique_ptr max(F O::*m, Args &&... args) { + this->assert_mapped_type(); + auto rows = this->select(sqlite_orm::max(m), std::forward(args)...); + if(!rows.empty()) { + return std::move(rows.front()); + } else { + return {}; + } + } + + /** + * MIN(x) query. + * @param m is a class member pointer (the same you passed into make_column). + * @return std::unique_ptr with min value or null if sqlite engine returned null. + */ + template::type> + std::unique_ptr min(F O::*m, Args &&... args) { + this->assert_mapped_type(); + auto rows = this->select(sqlite_orm::min(m), std::forward(args)...); + if(!rows.empty()) { + return std::move(rows.front()); + } else { + return {}; + } + } + + /** + * SUM(x) query. + * @param m is a class member pointer (the same you passed into make_column). + * @return std::unique_ptr with sum value or null if sqlite engine returned null. + */ + template::type> + std::unique_ptr sum(F O::*m, Args &&... args) { + this->assert_mapped_type(); + std::vector> rows = + this->select(sqlite_orm::sum(m), std::forward(args)...); + if(!rows.empty()) { + if(rows.front()) { + return std::make_unique(std::move(*rows.front())); + } else { + return {}; + } + } else { + return {}; + } + } + + /** + * TOTAL(x) query. + * @param m is a class member pointer (the same you passed into make_column). + * @return total value (the same as SUM but not nullable. More details here + * https://www.sqlite.org/lang_aggfunc.html) + */ + template + double total(F O::*m, Args &&... args) { + this->assert_mapped_type(); + auto rows = this->select(sqlite_orm::total(m), std::forward(args)...); + if(!rows.empty()) { + return std::move(rows.front()); + } else { + return {}; + } + } + + /** + * Select a single column into std::vector or multiple columns into std::vector>. + * For a single column use `auto rows = storage.select(&User::id, where(...)); + * For multicolumns use `auto rows = storage.select(columns(&User::id, &User::name), where(...)); + */ + template::type> + std::vector select(T m, Args... args) { + static_assert(!is_base_of_template::value || + std::tuple_size>::value == 0, + "Cannot use args with a compound operator"); + auto statement = this->prepare(sqlite_orm::select(std::move(m), std::forward(args)...)); + return this->execute(statement); + } + + template + typename std::enable_if::value, std::string>::type + dump(const T &preparedStatement) const { + using context_t = serializator_context; + context_t context{this->impl}; + return serialize(preparedStatement.t, context); + } + + /** + * Returns a string representation of object of a class mapped to the storage. + * Type of string has json-like style. + */ + template + typename std::enable_if::value, std::string>::type + dump(const O &o) { + auto &tImpl = this->get_impl(); + std::stringstream ss; + ss << "{ "; + using pair = std::pair; + std::vector pairs; + tImpl.table.for_each_column([&pairs, &o](auto &c) { + using column_type = typename std::decay::type; + using field_type = typename column_type::field_type; + pair p{c.name, std::string()}; + if(c.member_pointer) { + p.second = field_printer()(o.*c.member_pointer); + } else { + using getter_type = typename column_type::getter_type; + field_value_holder valueHolder{((o).*(c.getter))()}; + p.second = field_printer()(valueHolder.value); + } + pairs.push_back(move(p)); + }); + for(size_t i = 0; i < pairs.size(); ++i) { + auto &p = pairs[i]; + ss << p.first << " : '" << p.second << "'"; + if(i < pairs.size() - 1) { + ss << ", "; + } else { + ss << " }"; + } + } + return ss.str(); + } + + /** + * This is REPLACE (INSERT OR REPLACE) function. + * Also if you need to insert value with knows id you should + * also you this function instead of insert cause inserts ignores + * id and creates own one. + */ + template + void replace(const O &o) { + this->assert_mapped_type(); + auto statement = this->prepare(sqlite_orm::replace(std::ref(o))); + this->execute(statement); + } + + template + void replace_range(It from, It to) { + using O = typename std::iterator_traits::value_type; + this->assert_mapped_type(); + if(from == to) { + return; + } + + auto statement = this->prepare(sqlite_orm::replace_range(from, to)); + this->execute(statement); + } + + template + int insert(const O &o, columns_t cols) { + constexpr const size_t colsCount = std::tuple_size>::value; + static_assert(colsCount > 0, "Use insert or replace with 1 argument instead"); + this->assert_mapped_type(); + auto statement = this->prepare(sqlite_orm::insert(std::ref(o), std::move(cols))); + return int(this->execute(statement)); + } + + /** + * Insert routine. Inserts object with all non primary key fields in passed object. Id of passed + * object doesn't matter. + * @return id of just created object. + */ + template + int insert(const O &o) { + this->assert_mapped_type(); + auto statement = this->prepare(sqlite_orm::insert(std::ref(o))); + return int(this->execute(statement)); + } + + template + void insert_range(It from, It to) { + using O = typename std::iterator_traits::value_type; + this->assert_mapped_type(); + if(from == to) { + return; + } + + auto statement = this->prepare(sqlite_orm::insert_range(from, to)); + this->execute(statement); + } + + /** + * Change table name inside storage's schema info. This function does not + * affect database + */ + template + void rename_table(std::string name) { + this->assert_mapped_type(); + auto &tImpl = this->get_impl(); + tImpl.table.name = move(name); + } + + using storage_base::rename_table; + + /** + * Get table's name stored in storage's schema info. This function does not call + * any SQLite queries + */ + template + const std::string &tablename() const { + this->assert_mapped_type(); + auto &tImpl = this->get_impl(); + return tImpl.table.name; + } + + protected: + template + sync_schema_result sync_table(const storage_impl, Tss...> &tableImpl, sqlite3 *db, bool) { + auto res = sync_schema_result::already_in_sync; + using context_t = serializator_context; + context_t context{this->impl}; + auto query = serialize(tableImpl.table, context); + auto rc = sqlite3_exec(db, query.c_str(), nullptr, nullptr, nullptr); + if(rc != SQLITE_OK) { + throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), + sqlite3_errmsg(db)); + } + return res; + } + + template + sync_schema_result + sync_table(const storage_impl, Tss...> &tImpl, sqlite3 *db, bool preserve) { + auto res = sync_schema_result::already_in_sync; + + auto schema_stat = tImpl.schema_status(db, preserve); + if(schema_stat != decltype(schema_stat)::already_in_sync) { + if(schema_stat == decltype(schema_stat)::new_table_created) { + this->create_table(db, tImpl.table.name, tImpl); + res = decltype(res)::new_table_created; + } else { + if(schema_stat == sync_schema_result::old_columns_removed || + schema_stat == sync_schema_result::new_columns_added || + schema_stat == sync_schema_result::new_columns_added_and_old_columns_removed) { + + // get table info provided in `make_table` call.. + auto storageTableInfo = tImpl.table.get_table_info(); + + // now get current table info from db using `PRAGMA table_info` query.. + auto dbTableInfo = tImpl.get_table_info(tImpl.table.name, db); + + // this vector will contain pointers to columns that gotta be added.. + std::vector columnsToAdd; + + tImpl.get_remove_add_columns(columnsToAdd, storageTableInfo, dbTableInfo); + + if(schema_stat == sync_schema_result::old_columns_removed) { + + // extra table columns than storage columns + this->backup_table(db, tImpl, {}); + res = decltype(res)::old_columns_removed; + } + + if(schema_stat == sync_schema_result::new_columns_added) { + for(auto columnPointer: columnsToAdd) { + tImpl.add_column(*columnPointer, db); + } + res = decltype(res)::new_columns_added; + } + + if(schema_stat == sync_schema_result::new_columns_added_and_old_columns_removed) { + + // remove extra columns + this->backup_table(db, tImpl, columnsToAdd); + res = decltype(res)::new_columns_added_and_old_columns_removed; + } + } else if(schema_stat == sync_schema_result::dropped_and_recreated) { + this->drop_table_internal(tImpl.table.name, db); + this->create_table(db, tImpl.table.name, tImpl); + res = decltype(res)::dropped_and_recreated; + } + } + } + return res; + } + + public: + /** + * This is a cute function used to replace migration up/down functionality. + * It performs check storage schema with actual db schema and: + * * if there are excess tables exist in db they are ignored (not dropped) + * * every table from storage is compared with it's db analog and + * * if table doesn't exist it is being created + * * if table exists its colums are being compared with table_info from db and + * * if there are columns in db that do not exist in storage (excess) table will be dropped and + * recreated + * * if there are columns in storage that do not exist in db they will be added using `ALTER TABLE + * ... ADD COLUMN ...' command + * * if there is any column existing in both db and storage but differs by any of + * properties/constraints (type, pk, notnull, dflt_value) table will be dropped and recreated Be aware that + * `sync_schema` doesn't guarantee that data will not be dropped. It guarantees only that it will make db + * schema the same as you specified in `make_storage` function call. A good point is that if you have no db + * file at all it will be created and all tables also will be created with exact tables and columns you + * specified in `make_storage`, `make_table` and `make_column` call. The best practice is to call this + * function right after storage creation. + * @param preserve affects on function behaviour in case it is needed to remove a column. If it is `false` + * so table will be dropped if there is column to remove, if `true` - table is being copied into another + * table, dropped and copied table is renamed with source table name. Warning: sync_schema doesn't check + * foreign keys cause it is unable to do so in sqlite3. If you know how to get foreign key info please + * submit an issue https://github.com/fnc12/sqlite_orm/issues + * @return std::map with std::string key equal table name and `sync_schema_result` as value. + * `sync_schema_result` is a enum value that stores table state after syncing a schema. `sync_schema_result` + * can be printed out on std::ostream with `operator<<`. + */ + std::map sync_schema(bool preserve = false) { + auto con = this->get_connection(); + std::map result; + auto db = con.get(); + this->impl.for_each([&result, db, preserve, this](auto &tableImpl) { + auto res = this->sync_table(tableImpl, db, preserve); + result.insert({tableImpl.table.name, res}); + }); + return result; + } + + /** + * This function returns the same map that `sync_schema` returns but it + * doesn't perform `sync_schema` actually - just simulates it in case you want to know + * what will happen if you sync your schema. + */ + std::map sync_schema_simulate(bool preserve = false) { + auto con = this->get_connection(); + std::map result; + auto db = con.get(); + this->impl.for_each([&result, db, preserve](auto tableImpl) { + result.insert({tableImpl.table.name, tableImpl.schema_status(db, preserve)}); + }); + return result; + } + + /** + * Checks whether table exists in db. Doesn't check storage itself - works only with actual database. + * Note: table can be not mapped to a storage + * @return true if table with a given name exists in db, false otherwise. + */ + bool table_exists(const std::string &tableName) { + auto con = this->get_connection(); + return this->impl.table_exists(tableName, con.get()); + } + + template + prepared_statement_t> prepare(select_t sel) { + sel.highest_level = true; + auto con = this->get_connection(); + sqlite3_stmt *stmt; + auto db = con.get(); + using context_t = serializator_context; + context_t context{this->impl}; + context.skip_table_name = false; + context.replace_bindable_with_question = true; + auto query = serialize(sel, context); + if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { + return {std::move(sel), stmt, con}; + } else { + throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), + sqlite3_errmsg(db)); + } + } + + template + prepared_statement_t> prepare(get_all_t get) { + auto con = this->get_connection(); + sqlite3_stmt *stmt; + auto db = con.get(); + using context_t = serializator_context; + context_t context{this->impl}; + context.skip_table_name = false; + context.replace_bindable_with_question = true; + auto query = serialize(get, context); + if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { + return {std::move(get), stmt, con}; + } else { + throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), + sqlite3_errmsg(db)); + } + } + + template + prepared_statement_t> prepare(get_all_pointer_t get) { + auto con = this->get_connection(); + sqlite3_stmt *stmt; + auto db = con.get(); + using context_t = serializator_context; + context_t context{this->impl}; + context.skip_table_name = false; + context.replace_bindable_with_question = true; + auto query = serialize(get, context); + if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { + return {std::move(get), stmt, con}; + } else { + throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), + sqlite3_errmsg(db)); + } + } + +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + template + prepared_statement_t> prepare(get_all_optional_t get) { + auto con = this->get_connection(); + sqlite3_stmt *stmt; + auto db = con.get(); + using context_t = serializator_context; + context_t context{this->impl}; + context.skip_table_name = false; + context.replace_bindable_with_question = true; + auto query = serialize(get, context); + if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { + return {std::move(get), stmt, con}; + } else { + throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), + sqlite3_errmsg(db)); + } + } +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED + + template + prepared_statement_t, Wargs...>> + prepare(update_all_t, Wargs...> upd) { + auto con = this->get_connection(); + sqlite3_stmt *stmt; + auto db = con.get(); + using context_t = serializator_context; + context_t context{this->impl}; + context.skip_table_name = false; + context.replace_bindable_with_question = true; + auto query = serialize(upd, context); + if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { + return {std::move(upd), stmt, con}; + } else { + throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), + sqlite3_errmsg(db)); + } + } + + template + prepared_statement_t> prepare(remove_all_t rem) { + auto con = this->get_connection(); + sqlite3_stmt *stmt; + auto db = con.get(); + using context_t = serializator_context; + context_t context{this->impl}; + context.skip_table_name = false; + context.replace_bindable_with_question = true; + auto query = serialize(rem, context); + if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { + return {std::move(rem), stmt, std::move(con)}; + } else { + throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), + sqlite3_errmsg(db)); + } + } + + template + prepared_statement_t> prepare(get_t get) { + auto con = this->get_connection(); + sqlite3_stmt *stmt; + auto db = con.get(); + using context_t = serializator_context; + context_t context{this->impl}; + context.skip_table_name = false; + context.replace_bindable_with_question = true; + auto query = serialize(get, context); + if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { + return {std::move(get), stmt, con}; + } else { + throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), + sqlite3_errmsg(db)); + } + } + + template + prepared_statement_t> prepare(get_pointer_t get) { + auto con = this->get_connection(); + sqlite3_stmt *stmt; + auto db = con.get(); + using context_t = serializator_context; + context_t context{this->impl}; + context.skip_table_name = false; + context.replace_bindable_with_question = true; + auto query = serialize(get, context); + if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { + return {std::move(get), stmt, con}; + } else { + throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), + sqlite3_errmsg(db)); + } + } + +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + template + prepared_statement_t> prepare(get_optional_t get) { + auto con = this->get_connection(); + sqlite3_stmt *stmt; + auto db = con.get(); + using context_t = serializator_context; + context_t context{this->impl}; + context.skip_table_name = false; + context.replace_bindable_with_question = true; + auto query = serialize(get, context); + if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { + return {std::move(get), stmt, con}; + } else { + throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), + sqlite3_errmsg(db)); + } + } +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED + + template + prepared_statement_t> prepare(update_t upd) { + auto con = this->get_connection(); + sqlite3_stmt *stmt; + auto db = con.get(); + using context_t = serializator_context; + context_t context{this->impl}; + context.skip_table_name = false; + context.replace_bindable_with_question = true; + auto query = serialize(upd, context); + if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { + return {std::move(upd), stmt, con}; + } else { + throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), + sqlite3_errmsg(db)); + } + } + + template + prepared_statement_t> prepare(remove_t rem) { + auto con = this->get_connection(); + sqlite3_stmt *stmt; + auto db = con.get(); + using context_t = serializator_context; + context_t context{this->impl}; + context.skip_table_name = false; + context.replace_bindable_with_question = true; + auto query = serialize(rem, context); + if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { + return {std::move(rem), stmt, con}; + } else { + throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), + sqlite3_errmsg(db)); + } + } + + template + prepared_statement_t> prepare(insert_t ins) { + using object_type = typename expression_object_type::type; + this->assert_mapped_type(); + auto con = this->get_connection(); + sqlite3_stmt *stmt; + auto db = con.get(); + using context_t = serializator_context; + context_t context{this->impl}; + context.skip_table_name = false; + context.replace_bindable_with_question = true; + auto query = serialize(ins, context); + if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { + return {std::move(ins), stmt, con}; + } else { + throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), + sqlite3_errmsg(db)); + } + } + + template + prepared_statement_t> prepare(replace_t rep) { + auto con = this->get_connection(); + sqlite3_stmt *stmt; + using object_type = typename expression_object_type::type; + this->assert_mapped_type(); + auto db = con.get(); + using context_t = serializator_context; + context_t context{this->impl}; + context.skip_table_name = false; + context.replace_bindable_with_question = true; + auto query = serialize(rep, context); + if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { + return {std::move(rep), stmt, con}; + } else { + throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), + sqlite3_errmsg(db)); + } + } + + template + prepared_statement_t> prepare(insert_range_t statement) { + auto con = this->get_connection(); + sqlite3_stmt *stmt; + auto db = con.get(); + using context_t = serializator_context; + context_t context{this->impl}; + context.skip_table_name = false; + context.replace_bindable_with_question = true; + auto query = serialize(statement, context); + if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { + return {std::move(statement), stmt, con}; + } else { + throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), + sqlite3_errmsg(db)); + } + } + + template + prepared_statement_t> prepare(replace_range_t rep) { + auto con = this->get_connection(); + sqlite3_stmt *stmt; + auto db = con.get(); + using context_t = serializator_context; + context_t context{this->impl}; + context.skip_table_name = false; + context.replace_bindable_with_question = true; + auto query = serialize(rep, context); + if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { + return {std::move(rep), stmt, con}; + } else { + throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), + sqlite3_errmsg(db)); + } + } + + template + prepared_statement_t> prepare(insert_explicit ins) { + using object_type = typename expression_object_type::type; + this->assert_mapped_type(); + auto con = this->get_connection(); + sqlite3_stmt *stmt; + auto db = con.get(); + using context_t = serializator_context; + context_t context{this->impl}; + context.skip_table_name = false; + context.replace_bindable_with_question = true; + auto query = serialize(ins, context); + if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { + return {std::move(ins), stmt, con}; + } else { + throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), + sqlite3_errmsg(db)); + } + } + + template + int64 execute(const prepared_statement_t> &statement) { + using statement_type = typename std::decay::type; + using expression_type = typename statement_type::expression_type; + using object_type = typename expression_object_type::type; + auto index = 1; + auto con = this->get_connection(); + auto db = con.get(); + auto stmt = statement.stmt; + auto &tImpl = this->get_impl(); + auto &o = statement.t.obj; + sqlite3_reset(stmt); + iterate_tuple(statement.t.columns.columns, [&o, &index, &stmt, &tImpl, db](auto &m) { + using column_type = typename std::decay::type; + using field_type = typename column_result_t::type; + const field_type *value = tImpl.table.template get_object_field_pointer(o, m); + if(SQLITE_OK != statement_binder().bind(stmt, index++, *value)) { + throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), + sqlite3_errmsg(db)); + } + }); + if(sqlite3_step(stmt) == SQLITE_DONE) { + return sqlite3_last_insert_rowid(db); + } else { + throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), + sqlite3_errmsg(db)); + } + } + + template + void execute(const prepared_statement_t> &statement) { + using statement_type = typename std::decay::type; + using expression_type = typename statement_type::expression_type; + using object_type = typename expression_type::object_type; + auto &tImpl = this->get_impl(); + auto index = 1; + auto con = this->get_connection(); + auto db = con.get(); + auto stmt = statement.stmt; + sqlite3_reset(stmt); + for(auto it = statement.t.range.first; it != statement.t.range.second; ++it) { + auto &o = *it; + tImpl.table.for_each_column([&o, &index, &stmt, db](auto &c) { + using column_type = typename std::decay::type; + using field_type = typename column_type::field_type; + if(c.member_pointer) { + if(SQLITE_OK != statement_binder().bind(stmt, index++, o.*c.member_pointer)) { + throw std::system_error( + std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), + sqlite3_errmsg(db)); + } + } else { + using getter_type = typename column_type::getter_type; + field_value_holder valueHolder{((o).*(c.getter))()}; + if(SQLITE_OK != statement_binder().bind(stmt, index++, valueHolder.value)) { + throw std::system_error( + std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), + sqlite3_errmsg(db)); + } + } + }); + } + if(sqlite3_step(stmt) == SQLITE_DONE) { + //.. + } else { + throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), + sqlite3_errmsg(db)); + } + } + + template + void execute(const prepared_statement_t> &statement) { + using statement_type = typename std::decay::type; + using expression_type = typename statement_type::expression_type; + using object_type = typename expression_type::object_type; + auto index = 1; + auto con = this->get_connection(); + auto db = con.get(); + auto stmt = statement.stmt; + auto &tImpl = this->get_impl(); + sqlite3_reset(stmt); + for(auto it = statement.t.range.first; it != statement.t.range.second; ++it) { + auto &o = *it; + tImpl.table.for_each_column([&o, &index, &stmt, db](auto &c) { + if(!c.template has>()) { + using column_type = typename std::decay::type; + using field_type = typename column_type::field_type; + if(c.member_pointer) { + if(SQLITE_OK != + statement_binder().bind(stmt, index++, o.*c.member_pointer)) { + throw std::system_error( + std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), + sqlite3_errmsg(db)); + } + } else { + using getter_type = typename column_type::getter_type; + field_value_holder valueHolder{((o).*(c.getter))()}; + if(SQLITE_OK != statement_binder().bind(stmt, index++, valueHolder.value)) { + throw std::system_error( + std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), + sqlite3_errmsg(db)); + } + } + } + }); + } + if(sqlite3_step(stmt) == SQLITE_DONE) { + //.. + } else { + throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), + sqlite3_errmsg(db)); + } + } + + template + void execute(const prepared_statement_t> &statement) { + using statement_type = typename std::decay::type; + using expression_type = typename statement_type::expression_type; + using object_type = typename expression_object_type::type; + auto con = this->get_connection(); + auto db = con.get(); + auto stmt = statement.stmt; + auto index = 1; + auto &o = get_object(statement.t); + auto &tImpl = this->get_impl(); + sqlite3_reset(stmt); + tImpl.table.for_each_column([&o, &index, &stmt, db](auto &c) { + using column_type = typename std::decay::type; + using field_type = typename column_type::field_type; + if(c.member_pointer) { + if(SQLITE_OK != statement_binder().bind(stmt, index++, o.*c.member_pointer)) { + throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), + sqlite3_errmsg(db)); + } + } else { + using getter_type = typename column_type::getter_type; + field_value_holder valueHolder{((o).*(c.getter))()}; + if(SQLITE_OK != statement_binder().bind(stmt, index++, valueHolder.value)) { + throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), + sqlite3_errmsg(db)); + } + } + }); + if(sqlite3_step(stmt) == SQLITE_DONE) { + //.. + } else { + throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), + sqlite3_errmsg(db)); + } + } + + template + int64 execute(const prepared_statement_t> &statement) { + using statement_type = typename std::decay::type; + using expression_type = typename statement_type::expression_type; + using object_type = typename expression_object_type::type; + int64 res = 0; + auto con = this->get_connection(); + auto db = con.get(); + auto stmt = statement.stmt; + auto index = 1; + auto &tImpl = this->get_impl(); + auto &o = get_object(statement.t); + auto compositeKeyColumnNames = tImpl.table.composite_key_columns_names(); + sqlite3_reset(stmt); + tImpl.table.for_each_column([&o, &index, &stmt, &tImpl, &compositeKeyColumnNames, db](auto &c) { + if(tImpl.table._without_rowid || !c.template has>()) { + auto it = std::find(compositeKeyColumnNames.begin(), compositeKeyColumnNames.end(), c.name); + if(it == compositeKeyColumnNames.end()) { + using column_type = typename std::decay::type; + using field_type = typename column_type::field_type; + if(c.member_pointer) { + if(SQLITE_OK != + statement_binder().bind(stmt, index++, o.*c.member_pointer)) { + throw std::system_error( + std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), + sqlite3_errmsg(db)); + } + } else { + using getter_type = typename column_type::getter_type; + field_value_holder valueHolder{((o).*(c.getter))()}; + if(SQLITE_OK != statement_binder().bind(stmt, index++, valueHolder.value)) { + throw std::system_error( + std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), + sqlite3_errmsg(db)); + } + } + } + } + }); + if(sqlite3_step(stmt) == SQLITE_DONE) { + res = sqlite3_last_insert_rowid(db); + } else { + throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), + sqlite3_errmsg(db)); + } + return res; + } + + template + void execute(const prepared_statement_t> &statement) { + auto con = this->get_connection(); + auto db = con.get(); + auto stmt = statement.stmt; + auto index = 1; + sqlite3_reset(stmt); + iterate_ast(statement.t.ids, [stmt, &index, db](auto &v) { + using field_type = typename std::decay::type; + if(SQLITE_OK != statement_binder().bind(stmt, index++, v)) { + throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), + sqlite3_errmsg(db)); + } + }); + if(sqlite3_step(stmt) == SQLITE_DONE) { + // done.. + } else { + throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), + sqlite3_errmsg(db)); + } + } + + template + void execute(const prepared_statement_t> &statement) { + using statement_type = typename std::decay::type; + using expression_type = typename statement_type::expression_type; + using object_type = typename expression_object_type::type; + auto con = this->get_connection(); + auto db = con.get(); + auto &tImpl = this->get_impl(); + auto stmt = statement.stmt; + auto index = 1; + auto &o = get_object(statement.t); + sqlite3_reset(stmt); + tImpl.table.for_each_column([&o, stmt, &index, db](auto &c) { + if(!c.template has>()) { + using column_type = typename std::decay::type; + using field_type = typename column_type::field_type; + if(c.member_pointer) { + auto bind_res = statement_binder().bind(stmt, index++, o.*c.member_pointer); + if(SQLITE_OK != bind_res) { + throw std::system_error( + std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), + sqlite3_errmsg(db)); + } + } else { + using getter_type = typename column_type::getter_type; + field_value_holder valueHolder{((o).*(c.getter))()}; + if(SQLITE_OK != statement_binder().bind(stmt, index++, valueHolder.value)) { + throw std::system_error( + std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), + sqlite3_errmsg(db)); + } + } + } + }); + tImpl.table.for_each_column([&o, stmt, &index, db](auto &c) { + if(c.template has>()) { + using column_type = typename std::decay::type; + using field_type = typename column_type::field_type; + if(c.member_pointer) { + if(SQLITE_OK != statement_binder().bind(stmt, index++, o.*c.member_pointer)) { + throw std::system_error( + std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), + sqlite3_errmsg(db)); + } + } else { + using getter_type = typename column_type::getter_type; + field_value_holder valueHolder{((o).*(c.getter))()}; + if(SQLITE_OK != statement_binder().bind(stmt, index++, valueHolder.value)) { + throw std::system_error( + std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), + sqlite3_errmsg(db)); + } + } + } + }); + if(sqlite3_step(stmt) == SQLITE_DONE) { + // done.. + } else { + throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), + sqlite3_errmsg(db)); + } + } + + template + std::unique_ptr execute(const prepared_statement_t> &statement) { + auto &tImpl = this->get_impl(); + auto con = this->get_connection(); + auto db = con.get(); + auto stmt = statement.stmt; + auto index = 1; + sqlite3_reset(stmt); + iterate_ast(statement.t.ids, [stmt, &index, db](auto &v) { + using field_type = typename std::decay::type; + if(SQLITE_OK != statement_binder().bind(stmt, index++, v)) { + throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), + sqlite3_errmsg(db)); + } + }); + auto stepRes = sqlite3_step(stmt); + switch(stepRes) { + case SQLITE_ROW: { + auto res = std::make_unique(); + object_from_column_builder builder{*res, stmt}; + tImpl.table.for_each_column(builder); + return res; + } break; + case SQLITE_DONE: { + return {}; + } break; + default: { + throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), + sqlite3_errmsg(db)); + } + } + } + +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + template + std::optional execute(const prepared_statement_t> &statement) { + auto &tImpl = this->get_impl(); + auto con = this->get_connection(); + auto db = con.get(); + auto stmt = statement.stmt; + auto index = 1; + sqlite3_reset(stmt); + iterate_ast(statement.t.ids, [stmt, &index, db](auto &v) { + using field_type = typename std::decay::type; + if(SQLITE_OK != statement_binder().bind(stmt, index++, v)) { + throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), + sqlite3_errmsg(db)); + } + }); + auto stepRes = sqlite3_step(stmt); + switch(stepRes) { + case SQLITE_ROW: { + auto res = std::make_optional(); + object_from_column_builder builder{res.value(), stmt}; + tImpl.table.for_each_column(builder); + return res; + } break; + case SQLITE_DONE: { + return {}; + } break; + default: { + throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), + sqlite3_errmsg(db)); + } + } + } +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED + + template + T execute(const prepared_statement_t> &statement) { + auto &tImpl = this->get_impl(); + auto con = this->get_connection(); + auto db = con.get(); + auto stmt = statement.stmt; + auto index = 1; + sqlite3_reset(stmt); + iterate_ast(statement.t.ids, [stmt, &index, db](auto &v) { + using field_type = typename std::decay::type; + if(SQLITE_OK != statement_binder().bind(stmt, index++, v)) { + throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), + sqlite3_errmsg(db)); + } + }); + auto stepRes = sqlite3_step(stmt); + switch(stepRes) { + case SQLITE_ROW: { + T res; + object_from_column_builder builder{res, stmt}; + tImpl.table.for_each_column(builder); + return res; + } break; + case SQLITE_DONE: { + throw std::system_error(std::make_error_code(sqlite_orm::orm_error_code::not_found)); + } break; + default: { + throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), + sqlite3_errmsg(db)); + } + } + } + + template + void execute(const prepared_statement_t> &statement) { + auto con = this->get_connection(); + auto db = con.get(); + auto stmt = statement.stmt; + auto index = 1; + sqlite3_reset(stmt); + iterate_ast(statement.t.conditions, [stmt, &index, db](auto &node) { + using node_type = typename std::decay::type; + conditional_binder> binder{stmt, index}; + if(SQLITE_OK != binder(node)) { + throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), + sqlite3_errmsg(db)); + } + }); + if(sqlite3_step(stmt) == SQLITE_DONE) { + // done.. + } else { + throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), + sqlite3_errmsg(db)); + } + } + + template + void execute(const prepared_statement_t, Wargs...>> &statement) { + auto con = this->get_connection(); + auto db = con.get(); + auto stmt = statement.stmt; + auto index = 1; + sqlite3_reset(stmt); + iterate_tuple(statement.t.set.assigns, [&index, stmt, db](auto &setArg) { + iterate_ast(setArg, [&index, stmt, db](auto &node) { + using node_type = typename std::decay::type; + conditional_binder> binder{stmt, index}; + if(SQLITE_OK != binder(node)) { + throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), + sqlite3_errmsg(db)); + } + }); + }); + iterate_ast(statement.t.conditions, [stmt, &index, db](auto &node) { + using node_type = typename std::decay::type; + conditional_binder> binder{stmt, index}; + if(SQLITE_OK != binder(node)) { + throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), + sqlite3_errmsg(db)); + } + }); + if(sqlite3_step(stmt) == SQLITE_DONE) { + // done.. + } else { + throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), + sqlite3_errmsg(db)); + } + } + + template::type> + std::vector execute(const prepared_statement_t> &statement) { + auto con = this->get_connection(); + auto db = con.get(); + auto stmt = statement.stmt; + auto index = 1; + sqlite3_reset(stmt); + iterate_ast(statement.t, [stmt, &index, db](auto &node) { + using node_type = typename std::decay::type; + conditional_binder> binder{stmt, index}; + if(SQLITE_OK != binder(node)) { + throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), + sqlite3_errmsg(db)); + } + }); + std::vector res; + auto tableInfoPointer = this->impl.template find_table(); + int stepRes; + do { + stepRes = sqlite3_step(stmt); + switch(stepRes) { + case SQLITE_ROW: { + using table_info_pointer_t = typename std::remove_pointer::type; + using table_info_t = typename std::decay::type; + row_extractor_builder::value, table_info_t> + builder; + auto rowExtractor = builder(tableInfoPointer); + res.push_back(rowExtractor.extract(stmt, 0)); + } break; + case SQLITE_DONE: + break; + default: { + throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), + sqlite3_errmsg(db)); + } + } + } while(stepRes != SQLITE_DONE); + return res; + } + + template + R execute(const prepared_statement_t> &statement) { + auto &tImpl = this->get_impl(); + auto con = this->get_connection(); + auto db = con.get(); + auto stmt = statement.stmt; + auto index = 1; + sqlite3_reset(stmt); + iterate_ast(statement.t, [stmt, &index, db](auto &node) { + using node_type = typename std::decay::type; + conditional_binder> binder{stmt, index}; + if(SQLITE_OK != binder(node)) { + throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), + sqlite3_errmsg(db)); + } + }); + R res; + int stepRes; + do { + stepRes = sqlite3_step(stmt); + switch(stepRes) { + case SQLITE_ROW: { + T obj; + object_from_column_builder builder{obj, stmt}; + tImpl.table.for_each_column(builder); + res.push_back(std::move(obj)); + } break; + case SQLITE_DONE: + break; + default: { + throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), + sqlite3_errmsg(db)); + } + } + } while(stepRes != SQLITE_DONE); + return res; + } + + template + R execute(const prepared_statement_t> &statement) { + auto &tImpl = this->get_impl(); + auto con = this->get_connection(); + auto db = con.get(); + auto stmt = statement.stmt; + auto index = 1; + sqlite3_reset(stmt); + iterate_ast(statement.t, [stmt, &index, db](auto &node) { + using node_type = typename std::decay::type; + conditional_binder> binder{stmt, index}; + if(SQLITE_OK != binder(node)) { + throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), + sqlite3_errmsg(db)); + } + }); + R res; + int stepRes; + do { + stepRes = sqlite3_step(stmt); + switch(stepRes) { + case SQLITE_ROW: { + auto obj = std::make_unique(); + object_from_column_builder builder{*obj, stmt}; + tImpl.table.for_each_column(builder); + res.push_back(move(obj)); + } break; + case SQLITE_DONE: + break; + default: { + throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), + sqlite3_errmsg(db)); + } + } + } while(stepRes != SQLITE_DONE); + return res; + } + +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + template + R execute(const prepared_statement_t> &statement) { + auto &tImpl = this->get_impl(); + auto con = this->get_connection(); + auto db = con.get(); + auto stmt = statement.stmt; + auto index = 1; + sqlite3_reset(stmt); + iterate_ast(statement.t, [stmt, &index, db](auto &node) { + using node_type = typename std::decay::type; + conditional_binder> binder{stmt, index}; + if(SQLITE_OK != binder(node)) { + throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), + sqlite3_errmsg(db)); + } + }); + R res; + int stepRes; + do { + stepRes = sqlite3_step(stmt); + switch(stepRes) { + case SQLITE_ROW: { + auto obj = std::make_optional(); + object_from_column_builder builder{*obj, stmt}; + tImpl.table.for_each_column(builder); + res.push_back(move(obj)); + } break; + case SQLITE_DONE: + break; + default: { + throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), + sqlite3_errmsg(db)); + } + } + } while(stepRes != SQLITE_DONE); + return res; + } +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED + }; // struct storage_t + + template + struct is_storage : std::false_type {}; + + template + struct is_storage> : std::true_type {}; + } + + template + internal::storage_t make_storage(const std::string &filename, Ts... tables) { + return {filename, internal::storage_impl(tables...)}; + } + + /** + * sqlite3_threadsafe() interface. + */ + inline int threadsafe() { + return sqlite3_threadsafe(); + } +} +#pragma once + +#if defined(_MSC_VER) +#if defined(__RESTORE_MIN__) +__pragma(pop_macro("min")) +#undef __RESTORE_MIN__ +#endif +#if defined(__RESTORE_MAX__) + __pragma(pop_macro("max")) +#undef __RESTORE_MAX__ +#endif +#endif // defined(_MSC_VER) +#pragma once + +#include // std::tuple +#include // std::pair +#include // std::reference_wrapper + + // #include "conditions.h" + + // #include "operators.h" + + // #include "select_constraints.h" + + // #include "prepared_statement.h" + + // #include "optional_container.h" + + // #include "core_functions.h" + + namespace sqlite_orm { + + namespace internal { + + template + struct node_tuple { + using type = std::tuple; + }; + + template<> + struct node_tuple { + using type = std::tuple<>; + }; + + template + struct node_tuple, void> { + using type = typename node_tuple::type; + }; + + template + struct node_tuple, void> { + using node_type = where_t; + using type = typename node_tuple::type; + }; + + template + struct node_tuple::value>::type> { + using node_type = T; + using left_type = typename node_type::left_type; + using right_type = typename node_type::right_type; + using left_node_tuple = typename node_tuple::type; + using right_node_tuple = typename node_tuple::type; + using type = typename conc_tuple::type; + }; + + template + struct node_tuple, void> { + using node_type = binary_operator; + using left_type = typename node_type::left_type; + using right_type = typename node_type::right_type; + using left_node_tuple = typename node_tuple::type; + using right_node_tuple = typename node_tuple::type; + using type = typename conc_tuple::type; + }; + + template + struct node_tuple, void> { + using node_type = columns_t; + using type = typename conc_tuple::type...>::type; + }; + + template + struct node_tuple, void> { + using node_type = in_t; + using left_tuple = typename node_tuple::type; + using right_tuple = typename node_tuple::type; + using type = typename conc_tuple::type; + }; + + template + struct node_tuple::value>::type> { + using node_type = T; + using left_type = typename node_type::left_type; + using right_type = typename node_type::right_type; + using left_tuple = typename node_tuple::type; + using right_tuple = typename node_tuple::type; + using type = typename conc_tuple::type; + }; + + template + struct node_tuple, void> { + using node_type = select_t; + using columns_tuple = typename node_tuple::type; + using args_tuple = typename conc_tuple::type...>::type; + using type = typename conc_tuple::type; + }; + + template + struct node_tuple, void> { + using node_type = get_all_t; + using type = typename conc_tuple::type...>::type; + }; + + template + struct node_tuple, void> { + using node_type = get_all_pointer_t; + using type = typename conc_tuple::type...>::type; + }; + +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + template + struct node_tuple, void> { + using node_type = get_all_optional_t; + using type = typename conc_tuple::type...>::type; + }; +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED + + template + struct node_tuple, Wargs...>, void> { + using node_type = update_all_t, Wargs...>; + using set_tuple = typename conc_tuple::type...>::type; + using conditions_tuple = typename conc_tuple::type...>::type; + using type = typename conc_tuple::type; + }; + + template + struct node_tuple, void> { + using node_type = remove_all_t; + using type = typename conc_tuple::type...>::type; + }; + + template + struct node_tuple, void> { + using node_type = having_t; + using type = typename node_tuple::type; + }; + + template + struct node_tuple, void> { + using node_type = cast_t; + using type = typename node_tuple::type; + }; + + template + struct node_tuple, void> { + using node_type = exists_t; + using type = typename node_tuple::type; + }; + + template + struct node_tuple, void> { + using node_type = optional_container; + using type = typename node_tuple::type; + }; + + template<> + struct node_tuple, void> { + using node_type = optional_container; + using type = std::tuple<>; + }; + + template + struct node_tuple, void> { + using node_type = like_t; + using arg_tuple = typename node_tuple::type; + using pattern_tuple = typename node_tuple::type; + using escape_tuple = typename node_tuple::type; + using type = typename conc_tuple::type; + }; + + template + struct node_tuple, void> { + using node_type = glob_t; + using arg_tuple = typename node_tuple::type; + using pattern_tuple = typename node_tuple::type; + using type = typename conc_tuple::type; + }; + + template + struct node_tuple, void> { + using node_type = between_t; + using expression_tuple = typename node_tuple::type; + using lower_tuple = typename node_tuple::type; + using upper_tuple = typename node_tuple::type; + using type = typename conc_tuple::type; + }; + + template + struct node_tuple, void> { + using node_type = named_collate; + using type = typename node_tuple::type; + }; + + template + struct node_tuple, void> { + using node_type = is_null_t; + using type = typename node_tuple::type; + }; + + template + struct node_tuple, void> { + using node_type = is_not_null_t; + using type = typename node_tuple::type; + }; + + template + struct node_tuple, void> { + using node_type = negated_condition_t; + using type = typename node_tuple::type; + }; + + template + struct node_tuple, void> { + using node_type = core_function_t; + using type = typename conc_tuple::type...>::type; + }; + + template + struct node_tuple, void> { + using node_type = left_join_t; + using type = typename node_tuple::type; + }; + + template + struct node_tuple, void> { + using node_type = on_t; + using type = typename node_tuple::type; + }; + + template + struct node_tuple, void> { + using node_type = join_t; + using type = typename node_tuple::type; + }; + + template + struct node_tuple, void> { + using node_type = left_outer_join_t; + using type = typename node_tuple::type; + }; + + template + struct node_tuple, void> { + using node_type = inner_join_t; + using type = typename node_tuple::type; + }; + + template + struct node_tuple, void> { + using node_type = simple_case_t; + using case_tuple = typename node_tuple::type; + using args_tuple = typename conc_tuple::type...>::type; + using else_tuple = typename node_tuple::type; + using type = typename conc_tuple::type; + }; + + template + struct node_tuple, void> { + using node_type = std::pair; + using left_tuple = typename node_tuple::type; + using right_tuple = typename node_tuple::type; + using type = typename conc_tuple::type; + }; + + template + struct node_tuple, void> { + using node_type = as_t; + using type = typename node_tuple::type; + }; + + template + struct node_tuple, void> { + using node_type = limit_t; + using type = typename node_tuple::type; + }; + + template + struct node_tuple, void> { + using node_type = limit_t; + using type = typename conc_tuple::type, typename node_tuple::type>::type; + }; + + template + struct node_tuple, void> { + using node_type = limit_t; + using type = typename conc_tuple::type, typename node_tuple::type>::type; + }; + } +} +#pragma once + +#include // std::is_same, std::decay, std::remove_reference + +// #include "prepared_statement.h" + +// #include "ast_iterator.h" + +// #include "static_magic.h" + +// #include "expression_object_type.h" + +namespace sqlite_orm { + + template + auto &get(internal::prepared_statement_t> &statement) { + return std::get(statement.t.range); + } + + template + const auto &get(const internal::prepared_statement_t> &statement) { + return std::get(statement.t.range); + } + + template + auto &get(internal::prepared_statement_t> &statement) { + return std::get(statement.t.range); + } + + template + const auto &get(const internal::prepared_statement_t> &statement) { + return std::get(statement.t.range); + } + + template + auto &get(internal::prepared_statement_t> &statement) { + return internal::get_ref(std::get(statement.t.ids)); + } + + template + const auto &get(const internal::prepared_statement_t> &statement) { + return internal::get_ref(std::get(statement.t.ids)); + } + + template + auto &get(internal::prepared_statement_t> &statement) { + return internal::get_ref(std::get(statement.t.ids)); + } + + template + const auto &get(const internal::prepared_statement_t> &statement) { + return internal::get_ref(std::get(statement.t.ids)); + } + +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + template + auto &get(internal::prepared_statement_t> &statement) { + return internal::get_ref(std::get(statement.t.ids)); + } + + template + const auto &get(const internal::prepared_statement_t> &statement) { + return internal::get_ref(std::get(statement.t.ids)); + } +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED + + template + auto &get(internal::prepared_statement_t> &statement) { + return internal::get_ref(std::get(statement.t.ids)); + } + + template + const auto &get(const internal::prepared_statement_t> &statement) { + return internal::get_ref(std::get(statement.t.ids)); + } + + template + auto &get(internal::prepared_statement_t> &statement) { + static_assert(N == 0, "get<> works only with 0 argument for update statement"); + return internal::get_ref(statement.t.obj); + } + + template + const auto &get(const internal::prepared_statement_t> &statement) { + static_assert(N == 0, "get<> works only with 0 argument for update statement"); + return internal::get_ref(statement.t.obj); + } + + template + auto &get(internal::prepared_statement_t> &statement) { + static_assert(N == 0, "get<> works only with 0 argument for insert statement"); + return internal::get_ref(statement.t.obj); + } + + template + const auto &get(const internal::prepared_statement_t> &statement) { + static_assert(N == 0, "get<> works only with 0 argument for insert statement"); + return internal::get_ref(statement.t.obj); + } + + template + auto &get(internal::prepared_statement_t> &statement) { + static_assert(N == 0, "get<> works only with 0 argument for replace statement"); + return internal::get_ref(statement.t.obj); + } + + template + const auto &get(const internal::prepared_statement_t> &statement) { + static_assert(N == 0, "get<> works only with 0 argument for replace statement"); + return internal::get_ref(statement.t.obj); + } + + template + auto &get(internal::prepared_statement_t> &statement) { + static_assert(N == 0, "get<> works only with 0 argument for insert statement"); + return internal::get_ref(statement.t.obj); + } + + template + const auto &get(const internal::prepared_statement_t> &statement) { + static_assert(N == 0, "get<> works only with 0 argument for insert statement"); + return internal::get_ref(statement.t.obj); + } + + template + const auto &get(const internal::prepared_statement_t &statement) { + using statement_type = typename std::decay::type; + using expression_type = typename statement_type::expression_type; + using node_tuple = typename internal::node_tuple::type; + using bind_tuple = typename internal::bindable_filter::type; + using result_tupe = typename std::tuple_element::type; + const result_tupe *result = nullptr; + auto index = -1; + internal::iterate_ast(statement.t, [&result, &index](auto &node) { + using node_type = typename std::decay::type; + if(internal::is_bindable::value) { + ++index; + } + if(index == N) { + internal::static_if{}>([](auto &result, auto &node) { + result = const_cast::type>(&node); + })(result, node); + } + }); + return internal::get_ref(*result); + } + + template + auto &get(internal::prepared_statement_t &statement) { + using statement_type = typename std::decay::type; + using expression_type = typename statement_type::expression_type; + using node_tuple = typename internal::node_tuple::type; + using bind_tuple = typename internal::bindable_filter::type; + using result_tupe = typename std::tuple_element::type; + result_tupe *result = nullptr; + auto index = -1; + internal::iterate_ast(statement.t, [&result, &index](auto &node) { + using node_type = typename std::decay::type; + if(internal::is_bindable::value) { + ++index; + } + if(index == N) { + internal::static_if{}>([](auto &result, auto &node) { + result = const_cast::type>(&node); + })(result, node); + } + }); + return internal::get_ref(*result); + } +} diff --git a/src/settings.cpp b/src/settings.cpp index 07d0bc4..f030f8d 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -7,6 +7,7 @@ int settings::VERBOSITY = 1; int settings::LOGINPORT = 8001; bool settings::LOGINRANDCHARACTERS = false; +bool settings::APPROVEALLNAMES = true; int settings::SHARDPORT = 8002; std::string settings::SHARDSERVERIP = "127.0.0.1"; @@ -35,6 +36,7 @@ void settings::init() { return; } + APPROVEALLNAMES = reader.GetBoolean("", "acceptallcustomnames", APPROVEALLNAMES); VERBOSITY = reader.GetInteger("", "verbosity", VERBOSITY); LOGINPORT = reader.GetInteger("login", "port", LOGINPORT); LOGINRANDCHARACTERS = reader.GetBoolean("login", "randomcharacters", LOGINRANDCHARACTERS); diff --git a/src/settings.hpp b/src/settings.hpp index e15b70c..7accc2a 100644 --- a/src/settings.hpp +++ b/src/settings.hpp @@ -4,6 +4,7 @@ namespace settings { extern int VERBOSITY; extern int LOGINPORT; extern bool LOGINRANDCHARACTERS; + extern bool APPROVEALLNAMES; extern int SHARDPORT; extern std::string SHARDSERVERIP; extern int PLAYERDISTANCE;